Spaces:
Running
Running
| from ai_module import ask_ai, PROVIDERS, DEFAULT_PROVIDER, detect_suggested_provider | |
| import os | |
| import sys | |
| import random | |
| import re | |
| import json | |
| from datetime import datetime | |
| # ββ Version ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| VERSION = "1.3.0" | |
| # ββ ANSI Colors ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CYAN = "\033[96m" | |
| GREEN = "\033[92m" | |
| YELLOW = "\033[93m" | |
| RED = "\033[91m" | |
| MAGENTA = "\033[95m" | |
| BLUE = "\033[94m" | |
| WHITE = "\033[97m" | |
| ORANGE = "\033[33m" | |
| PINK = "\033[38;5;213m" | |
| BOLD = "\033[1m" | |
| DIM = "\033[2m" | |
| RESET = "\033[0m" | |
| # ββ Color name β ANSI map (used by `color` command) ββββββββββββ | |
| COLOR_MAP = { | |
| "cyan": CYAN, | |
| "green": GREEN, | |
| "yellow": YELLOW, | |
| "red": RED, | |
| "magenta": MAGENTA, | |
| "blue": BLUE, | |
| "white": WHITE, | |
| "orange": ORANGE, | |
| "pink": PINK, | |
| } | |
| # ββ Provider display colors βββββββββββββββββββββββββββββββββββββ | |
| PROVIDER_COLORS = { | |
| "chatgpt": GREEN, | |
| "mini": YELLOW, | |
| "reasoning": BLUE, | |
| } | |
| # ββ Themes βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| THEMES = { | |
| "default": {"user": YELLOW, "ai": GREEN, "system": CYAN}, | |
| "dark": {"user": PINK, "ai": PINK, "system": PINK}, | |
| "hacker": {"user": GREEN, "ai": GREEN, "system": GREEN}, | |
| "ocean": {"user": BLUE, "ai": BLUE, "system": BLUE}, | |
| "sunset": {"user": ORANGE, "ai": ORANGE, "system": ORANGE}, | |
| } | |
| PROMPT_STYLES = ["classic", "arrow", "simple", "minimal", "bracket"] | |
| AI_NAMES = [ | |
| "Nova", "Orion", "Atlas", "Echo", "Zenith", | |
| "Pixel", "Astra", "Cosmo", "Nimbus", "Luna", | |
| ] | |
| # ββ Mood map (prefixed to user query before ask_ai) ββββββββββββ | |
| MOODS = { | |
| "default": "", | |
| "concise": ( | |
| "Be extremely concise. Answer in one or two sentences max. " | |
| "No fluff, no preamble, straight to the point. " | |
| ), | |
| "formal": ( | |
| "Respond in a polished, professional, and formal tone. " | |
| "Use complete sentences, avoid contractions, and maintain decorum. " | |
| ), | |
| "friendly": ( | |
| "Respond in a warm, casual, and upbeat tone β like texting a good friend. " | |
| "Use light humor where natural, keep it approachable and encouraging. " | |
| ), | |
| "funny": ( | |
| "Reply with wit, sarcasm, and sharp humor. Slip in a clever joke or pun if it fits. " | |
| "Keep it entertaining while still being helpful. " | |
| ), | |
| "teacher": ( | |
| "Explain as if the person is a complete beginner with zero prior knowledge. " | |
| "Use simple language, relatable analogies, and short examples. Avoid jargon. " | |
| ), | |
| "expert": ( | |
| "Assume the person is a domain expert. Skip basics, use precise technical language, " | |
| "reference advanced concepts freely, and be thorough. " | |
| ), | |
| } | |
| # ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def sys_msg(color, text): | |
| """Print a colored system message.""" | |
| print(f"{color}{text}{RESET}") | |
| def divider(color=DIM): | |
| print(f"{color}{'β' * 44}{RESET}") | |
| # ββ Hardcoded Badass Jokes (fallback + random pool) ββββββββββββ | |
| HARDCODED_JOKES = [ | |
| ( | |
| "I asked God for a bike, but I know God doesn't work that way.", | |
| "So I stole a bike and asked for forgiveness." | |
| ), | |
| ( | |
| "My therapist says I have a preoccupation with vengeance.", | |
| "We'll see about that." | |
| ), | |
| ( | |
| "Why do orphans love boomerangs?", | |
| "It's the only thing that ever comes back." | |
| ), | |
| ( | |
| "I told my wife she should embrace her mistakes.", | |
| "She gave me a hug." | |
| ), | |
| ( | |
| "What's the difference between a pizza and my ex?", | |
| "A pizza can actually feed a family." | |
| ), | |
| ( | |
| "Scientists say the universe is made of protons, neutrons, and electrons.", | |
| "They forgot to mention morons." | |
| ), | |
| ( | |
| "My doctor told me I need to watch my drinking.", | |
| "So now I do it in front of a mirror." | |
| ), | |
| ( | |
| "I tried to write a joke about unemployment.", | |
| "It didn't work out." | |
| ), | |
| ( | |
| "What's the difference between a good programmer and a bad programmer?", | |
| "About three Stack Overflow tabs." | |
| ), | |
| ( | |
| "Why did the existentialist fail the exam?", | |
| "He left it blank β nothing has meaning anyway." | |
| ), | |
| ( | |
| "My wife said I had to stop acting like a flamingo.", | |
| "I had to put my foot down." | |
| ), | |
| ( | |
| "How many narcissists does it take to change a light bulb?", | |
| "One. They hold the bulb and wait for the world to revolve around them." | |
| ), | |
| ( | |
| "I told my boss three companies were after me and I needed a raise.", | |
| "He asked which companies. I said: gas, electric, and water." | |
| ), | |
| ( | |
| "Why did the software developer go broke?", | |
| "Because he cleared his cache and cookies and lost everything." | |
| ), | |
| ( | |
| "My life coach told me to write a letter to the person who hurt me most.", | |
| "I'm now waiting for the mailman to deliver it to myself." | |
| ), | |
| ] | |
| # ββ Hardcoded Savage Roasts (fallback + random pool) βββββββββββ | |
| HARDCODED_ROASTS = [ | |
| "You're not the dumbest person alive, but you better hope they don't die.", | |
| "Your birth certificate is an apology letter from the hospital.", | |
| "You have the energy of a low-battery notification β always dying, never actually shutting up.", | |
| "You're the reason instructions on shampoo bottles say 'repeat' β someone had to dumb it down for you.", | |
| "Your LinkedIn says 'open to opportunities' because even your career ghosted you.", | |
| "Somewhere a tree is working overtime producing oxygen just for you, and it deeply regrets it.", | |
| "You're the human equivalent of a software update at 2 AM β nobody asked for you, you take forever, and you barely improve anything.", | |
| "You're proof that evolution occasionally hits Ctrl+Z.", | |
| "Your parents looked at you and invented the phrase 'unconditional love' β because no conditions would've kept them around.", | |
| "You're like a cloud β when you disappear, it's a beautiful day.", | |
| "The only thing your ex misses about you is the feeling of not having you around.", | |
| "You have a face made for radio and a voice made for text.", | |
| "I'd roast you properly but my therapist says I need to stop setting fire to garbage.", | |
| "You're the type who gets outsmarted by a CAPTCHA on a daily basis.", | |
| "NASA is studying you β not because you're special, but because they've never seen a black hole with a LinkedIn profile.", | |
| ] | |
| # ββ Joke Style Selector ββββββββββββββββββββββββββββββββββββββββ | |
| JOKE_STYLES = [ | |
| "misdirection β the setup sounds totally innocent, then the punchline is a gut punch from nowhere", | |
| "dark irony β the darkest possible interpretation of an everyday situation", | |
| "absurdist logic taken to its horrifying natural conclusion", | |
| "a fake-deep observation about life that ends in pure nihilistic chaos", | |
| "self-aware meta humor where the joke eats itself alive", | |
| "a savage comparison that makes you feel personally attacked and laughing at the same time", | |
| ] | |
| # ββ Roast Style Selector βββββββββββββββββββββββββββββββββββββββ | |
| ROAST_STYLES = [ | |
| "surgical precision β one quiet, specific observation that dismantles their entire identity", | |
| "fake compliment that flips into a devastating gut punch in the last four words", | |
| "existential dread β make them question why they were born and what they're doing with their life", | |
| "statistical improbability β frame how astronomically unlikely it is that they turned out this bad", | |
| "tech/software metaphor β compare them to the most broken, unwanted piece of software imaginable", | |
| "comparison to something universally disappointing, useless, or abandoned", | |
| ] | |
| # ββ AI-powered Joke ββββββββββββββββββββββββββββββββββββββββββββ | |
| def get_ai_joke(): | |
| """70% AI-generated, 30% hardcoded β always nuclear-level funny.""" | |
| # 30% chance: use a hardcoded banger for instant delivery | |
| if random.random() < 0.30: | |
| return random.choice(HARDCODED_JOKES) | |
| style = random.choice(JOKE_STYLES) | |
| examples = random.sample(HARDCODED_JOKES, 2) | |
| ex1 = f"Q: {examples[0][0]}\nA: {examples[0][1]}" | |
| ex2 = f"Q: {examples[1][0]}\nA: {examples[1][1]}" | |
| prompt = ( | |
| "You are a battle-hardened stand-up comedian who has performed at the Comedy Cellar, " | |
| "roasted celebrities on live TV, and never once told a safe joke. " | |
| "Your jokes have made people spit out drinks, miss their subway stop, and text the joke " | |
| "to five people immediately. You do not do mild. You do not do safe. " | |
| "You only produce jokes that hit like a freight train.\n\n" | |
| f"Style for this joke: {style}\n\n" | |
| "Here are two examples of the MINIMUM quality I expect:\n" | |
| f"{ex1}\n" | |
| f"{ex2}\n\n" | |
| "Now write ONE original joke that is FUNNIER and MORE UNEXPECTED than both examples above. " | |
| "The punchline must be something nobody sees coming. " | |
| "NO recycled jokes. NO safe territory. If it doesn't make someone wheeze with laughter " | |
| "or feel slightly attacked, it's not good enough β try harder.\n\n" | |
| "Format EXACTLY as:\n" | |
| "Q: <setup>\n" | |
| "A: <punchline>\n" | |
| "Output ONLY those two lines. Zero extra text. Zero preamble." | |
| ) | |
| try: | |
| response = ask_ai(prompt).strip() | |
| lines = [l.strip() for l in response.splitlines() if l.strip()] | |
| setup = punchline = None | |
| for line in lines: | |
| if line.lower().startswith("q:"): | |
| setup = line[2:].strip() | |
| elif line.lower().startswith("a:"): | |
| punchline = line[2:].strip() | |
| if setup and punchline: | |
| return setup, punchline | |
| if len(lines) >= 2: | |
| return lines[0], lines[1] | |
| # Last resort: hardcoded banger | |
| return random.choice(HARDCODED_JOKES) | |
| except Exception: | |
| return random.choice(HARDCODED_JOKES) | |
| # ββ AI-powered Roast βββββββββββββββββββββββββββββββββββββββββββ | |
| def get_ai_roast(target): | |
| """70% AI-generated, 30% hardcoded β always soul-destroying.""" | |
| subject = "me" if target in ("you", "me") else target | |
| # 30% chance: fire a hardcoded nuke instantly | |
| if random.random() < 0.30: | |
| return random.choice(HARDCODED_ROASTS) | |
| style = random.choice(ROAST_STYLES) | |
| examples = random.sample(HARDCODED_ROASTS, 3) | |
| ex_block = "\n".join(f"β {e}" for e in examples) | |
| prompt = ( | |
| f"You are the most feared roast master in history. You have made celebrities cry on stage, " | |
| f"ended careers with a single sentence, and turned whole rooms silent with one line. " | |
| f"Your roasts are legendary because they feel PERSONAL β like you've studied the target " | |
| f"for years and found the one sentence that exposes everything.\n\n" | |
| f"Target: {subject}\n" | |
| f"Style: {style}\n\n" | |
| f"Here are examples of the MINIMUM brutality I expect:\n" | |
| f"{ex_block}\n\n" | |
| f"Now write ONE roast for {subject} that is MORE devastating than any example above. " | |
| "Rules:\n" | |
| "β Must feel specific and personal, not generic.\n" | |
| "β Must be clever AND cruel β the kind that gets screenshotted and shared.\n" | |
| "β Must sting for days. They should question their life choices after reading it.\n" | |
| "β Should land like a sniper shot β quiet, precise, absolutely fatal.\n" | |
| "β No filler words, no warm-up. Pure payload from the first word.\n\n" | |
| "Output ONLY the roast. One to two sentences max. " | |
| "No intro, no label, no emoji, no explanation. Go for the jugular." | |
| ) | |
| try: | |
| result = ask_ai(prompt).strip().strip('"').strip("'") | |
| return result if result else random.choice(HARDCODED_ROASTS) | |
| except Exception: | |
| return random.choice(HARDCODED_ROASTS) | |
| # ββ Banner βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def print_banner(ai_name, theme_colors): | |
| c = theme_colors | |
| print(f""" | |
| {BOLD}{c['system']} | |
| ββββββββββββββββββββββββββββββββββββββββββββ | |
| β π€ AI CLI Assistant β | |
| β Type 'help' to get started β | |
| ββββββββββββββββββββββββββββββββββββββββββββ | |
| {RESET}{DIM}AI Assistant:{RESET} {BOLD}{c['ai']}{ai_name}{RESET} | |
| """) | |
| # ββ Provider info display βββββββββββββββββββββββββββββββββββββββ | |
| def print_providers(current_provider): | |
| print(f"\n{BOLD}Available AI Providers:{RESET}\n") | |
| for key, info in PROVIDERS.items(): | |
| col = PROVIDER_COLORS[key] | |
| marker = f" {BOLD}{DIM}β active{RESET}" if key == current_provider else "" | |
| print(f" {col}{info['icon']} {BOLD}{info['label']:<22}{RESET}" | |
| f" {DIM}{info['tagline']}{RESET}{marker}") | |
| print(f" {DIM}key: {key} best for: {info['best_for']}{RESET}") | |
| print(f"\n {DIM}Tip: type provider <name> to switch " | |
| f"(e.g. provider mini){RESET}\n") | |
| # ββ Dynamic help (reads live state) ββββββββββββββββββββββββββββ | |
| def build_help(theme_colors): | |
| Y = theme_colors["user"] | |
| C = theme_colors["system"] | |
| return f""" | |
| {BOLD}{C}AI CLI β Command Reference{RESET} | |
| {BOLD}General{RESET} | |
| {Y}help{RESET} Show this command list | |
| {Y}help <command>{RESET} Get details on a specific command | |
| {Y}exit / quit{RESET} Exit the CLI | |
| {Y}clear{RESET} Clear the terminal screen | |
| {Y}version{RESET} Show CLI version | |
| {Y}history{RESET} Show full chat history | |
| {Y}clearhistory{RESET} Wipe the chat history clean | |
| {BOLD}Customization{RESET} | |
| {Y}rename <n>{RESET} Rename the AI (e.g. rename Jarvis) | |
| {Y}randomname{RESET} Give the AI a random name | |
| {Y}theme <n>{RESET} Switch color theme (e.g. theme ocean) | |
| {Y}themes{RESET} List all available themes | |
| {Y}color <element> <color>{RESET} Fine-tune a single color | |
| Elements : user Β· ai Β· system | |
| Colors : {', '.join(COLOR_MAP)} | |
| Example : color ai blue | |
| {Y}prompt <style>{RESET} Change the input prompt style | |
| {Y}prompts{RESET} List all prompt styles | |
| {Y}mood <n>{RESET} Set AI response mood (e.g. mood funny) | |
| {Y}moods{RESET} List all available moods | |
| {Y}timestamp on/off{RESET} Show/hide time on AI replies | |
| {Y}banner on/off / banner{RESET} Toggle or re-show the banner | |
| {Y}resetui{RESET} Reset all CLI settings to defaults | |
| {BOLD}Utilities{RESET} | |
| {Y}whoami{RESET} Show current CLI settings | |
| {Y}tips{RESET} Show usage tips | |
| {Y}examples{RESET} Show example commands | |
| {Y}echo <text>{RESET} Print text back to screen | |
| {Y}roast / roast <n>{RESET} Get roasted or roast someone π₯ | |
| {Y}joke {RESET} Hear a random joke π | |
| {Y}save txt{RESET} Save to chat_history.txt | |
| {Y}save json{RESET} Save to chat_history.json | |
| {Y}metrics{RESET} Show message count stats | |
| {BOLD}Input Rules{RESET} | |
| β’ Max {BOLD}1000{RESET} characters per message | |
| β’ Min {BOLD}2{RESET} characters required | |
| β’ Repeated identical messages are blocked | |
| β’ Gibberish detection (repeating chars) warns you | |
| """ | |
| # ββ Per-command detailed help βββββββββββββββββββββββββββββββββββ | |
| COMMAND_HELP = { | |
| "theme": "Usage: theme <n>\nSwitches the color theme.\nExample: theme ocean\nUse 'themes' to list all options.", | |
| "color": "Usage: color <element> <colorname>\nElements: user, ai, system\nColors: " + ", ".join(COLOR_MAP) + "\nExample: color ai blue", | |
| "prompt": "Usage: prompt <style>\nChanges the input prompt look.\nStyles: classic, arrow, simple, minimal, bracket\nExample: prompt bracket", | |
| "rename": "Usage: rename <n>\nRenames the AI assistant.\nExample: rename Jarvis", | |
| "mood": "Usage: mood <n>\nPrefixes your message with a tone instruction.\nExample: mood concise\nUse 'moods' to see all options.", | |
| "timestamp": "Usage: timestamp on OR timestamp off\nShows or hides the time next to AI replies.", | |
| "whoami": "Displays your current CLI settings (theme, mood, prompt style, provider, etc.)", | |
| "resetui": "Resets all CLI settings back to their defaults (including provider β chatgpt).", | |
| "echo": "Usage: echo <text>\nPrints the given text back to the terminal.", | |
| "provider": ("Usage: provider <name>\n" | |
| "Switches the active AI provider.\n" | |
| "Available providers:\n" | |
| " chatgpt β ChatGPT GPT-4 (best for research)\n" | |
| " mini β mini Sonnet (best for coding)\n" | |
| " reasoning β reasoning 1.5 Flash (best for mathematics)\n" | |
| "Example: provider mini\n" | |
| "Use 'providers' to see the full list with descriptions."), | |
| } | |
| # ββ Input Validation βββββββββββββββββββββββββββββββββββββββββββ | |
| def validate_input(text, last_input): | |
| text = text.strip() | |
| # Empty | |
| if not text: | |
| return False, f"{RED}β Input cannot be empty.{RESET}" | |
| # Too short | |
| if len(text) < 2: | |
| return False, f"{RED}β Input too short (min 2 characters).{RESET}" | |
| # Too long | |
| if len(text) > 1000: | |
| return False, f"{RED}β Input too long (max 1000 characters).{RESET}" | |
| # Spam: same message repeated | |
| if text.lower() == last_input.lower() and last_input: | |
| return False, f"{YELLOW}β Same message as last time. Try something new.{RESET}" | |
| # Gibberish: single char repeated 6+ times (e.g. "aaaaaa", "......") | |
| if re.fullmatch(r"(.)\1{5,}", text): | |
| return False, f"{YELLOW}β Looks like gibberish. Please enter a proper message.{RESET}" | |
| # All digits only | |
| if text.isdigit(): | |
| return False, f"{YELLOW}β Message is just numbers. Please enter a question or statement.{RESET}" | |
| return True, "" | |
| # ββ Prompt builder βββββββββββββββββββββββββββββββββββββββββββββ | |
| def build_prompt(prompt_style, theme_colors): | |
| if prompt_style == "arrow": | |
| return f"{theme_colors['user']}β {RESET}" | |
| elif prompt_style == "simple": | |
| return "> " | |
| elif prompt_style == "minimal": | |
| return " " | |
| elif prompt_style == "bracket": | |
| return f"{theme_colors['user']}[you]{RESET} " | |
| else: # classic | |
| return f"{BOLD}{theme_colors['user']}You:{RESET} " | |
| class ChatHistory: | |
| def __init__(self): | |
| self.history = [] | |
| self.total_messages = 0 | |
| def add(self, role, content): | |
| msg = { | |
| "role": role, | |
| "content": content, | |
| "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| } | |
| self.history.append(msg) | |
| self.total_messages += 1 | |
| def show(self): | |
| if not self.history: | |
| print("No chat history.\n") | |
| return | |
| print("\n--- Chat History ---") | |
| for m in self.history: | |
| print(f"[{m['time']}] {m['role'].upper()}: {m['content']}") | |
| print() | |
| def save_txt(self, file="chat_history.txt"): | |
| with open(file, "w", encoding="utf-8") as f: | |
| for m in self.history: | |
| f.write(f"[{m['time']}] {m['role']}: {m['content']}\n") | |
| print(f"Saved to {file}\n") | |
| def save_json(self, file="chat_history.json"): | |
| with open(file, "w", encoding="utf-8") as f: | |
| json.dump(self.history, f, indent=4) | |
| print(f"Saved to {file}\n") | |
| def metrics(self): | |
| user = len([m for m in self.history if m["role"] == "user"]) | |
| ai = len([m for m in self.history if m["role"] == "ai"]) | |
| print("\n--- Metrics ---") | |
| print(f"Total messages: {self.total_messages}") | |
| print(f"User messages : {user}") | |
| print(f"AI messages : {ai}\n") | |
| def clear(self): | |
| self.history = [] | |
| self.total_messages = 0 | |
| print("History cleared.\n") | |
| # ββ Main CLI βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def main(): | |
| chat = ChatHistory() | |
| # ββ Session state βββββββββββββββββββββββββββββββββββββββββ | |
| ai_name = "AI" | |
| theme = "default" | |
| prompt_style = "classic" | |
| banner_enabled = True | |
| mood = "default" | |
| timestamp_on = False | |
| last_input = "" | |
| provider = DEFAULT_PROVIDER # β active AI provider | |
| theme_colors = THEMES[theme].copy() | |
| if banner_enabled: | |
| print_banner(ai_name, theme_colors) | |
| while True: | |
| prompt_text = build_prompt(prompt_style, theme_colors) | |
| try: | |
| sys.stdout.write(prompt_text) | |
| sys.stdout.flush() | |
| raw = input("").strip() | |
| except (KeyboardInterrupt, EOFError): | |
| print("\nGoodbye!") | |
| break | |
| cmd = raw.lower() | |
| # ββ Exit ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if cmd in ("exit", "quit"): | |
| sys_msg(theme_colors["system"], "Goodbye! π") | |
| break | |
| # ββ Help ββββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "help": | |
| print(build_help(theme_colors)) | |
| continue | |
| elif cmd.startswith("help "): | |
| topic = raw[5:].strip().lower() | |
| if topic in COMMAND_HELP: | |
| divider(theme_colors["system"]) | |
| print(f"\n{BOLD}{theme_colors['system']}{topic}{RESET}\n") | |
| print(COMMAND_HELP[topic]) | |
| divider(theme_colors["system"]) | |
| print() | |
| else: | |
| sys_msg(RED, f"β No detailed help for '{topic}'. Try 'help' for full list.") | |
| continue | |
| # ββ Version βββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "version": | |
| sys_msg(theme_colors["system"], f"AI CLI v{VERSION}") | |
| continue | |
| # ββ Clear βββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "clear": | |
| os.system("cls" if os.name == "nt" else "clear") | |
| continue | |
| # ββ Provider commands βββββββββββββββββββββββββββββββββ | |
| elif cmd == "providers": | |
| print_providers(provider) | |
| continue | |
| elif cmd == "provider": | |
| col = PROVIDER_COLORS[provider] | |
| info = PROVIDERS[provider] | |
| sys_msg(col, f"{info['icon']} Active provider: {BOLD}{info['label']}{RESET}{col} " | |
| f"β {info['tagline']}") | |
| sys_msg(DIM, " Use 'providers' to list all, or 'provider <name>' to switch.") | |
| print() | |
| continue | |
| elif cmd.startswith("provider "): | |
| new_provider = raw.split()[1].lower() | |
| if new_provider not in PROVIDERS: | |
| sys_msg(RED, f"β Unknown provider '{new_provider}'.") | |
| sys_msg(DIM, f" Available: {', '.join(PROVIDERS)}") | |
| else: | |
| provider = new_provider | |
| info = PROVIDERS[provider] | |
| col = PROVIDER_COLORS[provider] | |
| sys_msg(col, f"β Switched to {info['icon']} {BOLD}{info['label']}{RESET}{col}" | |
| f" β {info['tagline']}") | |
| print() | |
| continue | |
| # ββ Rename AI βββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "rename": | |
| sys_msg(YELLOW, "Usage: rename <n> e.g. rename Jarvis") | |
| continue | |
| elif cmd.startswith("rename "): | |
| ai_name = raw[7:].strip() or "AI" | |
| sys_msg(theme_colors["system"], f"β AI renamed to '{ai_name}'") | |
| continue | |
| # ββ Random name βββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "randomname": | |
| ai_name = random.choice(AI_NAMES) | |
| sys_msg(theme_colors["system"], f"β AI renamed to '{ai_name}'") | |
| continue | |
| # ββ Themes list βββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "themes": | |
| sys_msg(BOLD, "Available themes:") | |
| for t, cols in THEMES.items(): | |
| marker = " β active" if t == theme else "" | |
| print(f" {cols['ai']}β{RESET} {t}{DIM}{marker}{RESET}") | |
| print() | |
| continue | |
| # ββ Theme change ββββββββββββββββββββββββββββββββββββββ | |
| elif cmd.startswith("theme "): | |
| new_theme = raw.split()[1].lower() | |
| if new_theme not in THEMES: | |
| sys_msg(RED, f"β Unknown theme '{new_theme}'.") | |
| sys_msg(DIM, " Use 'themes' to see options.") | |
| else: | |
| theme = new_theme | |
| theme_colors = THEMES[theme].copy() | |
| sys_msg(theme_colors["system"], f"β Theme switched to '{theme}'") | |
| continue | |
| # ββ Fine-grained color customization ββββββββββββββββββ | |
| elif cmd == "colors": | |
| sys_msg(BOLD, "Available colors:") | |
| for name, code in COLOR_MAP.items(): | |
| print(f" {code}β {name}{RESET}") | |
| print() | |
| continue | |
| elif cmd.startswith("color "): | |
| parts = raw.split() | |
| if len(parts) != 3: | |
| sys_msg(YELLOW, "Usage: color <element> <color>") | |
| sys_msg(DIM, " Elements: user Β· ai Β· system") | |
| sys_msg(DIM, " Example : color ai blue") | |
| else: | |
| element, color_name = parts[1].lower(), parts[2].lower() | |
| if element not in ("user", "ai", "system"): | |
| sys_msg(RED, f"β Unknown element '{element}'. Use: user, ai, system") | |
| elif color_name not in COLOR_MAP: | |
| sys_msg(RED, f"β Unknown color '{color_name}'.") | |
| sys_msg(DIM, f" Available: {', '.join(COLOR_MAP)}") | |
| else: | |
| theme_colors[element] = COLOR_MAP[color_name] | |
| sys_msg(theme_colors["system"], f"β '{element}' color set to {COLOR_MAP[color_name]}{color_name}{RESET}") | |
| continue | |
| # ββ Prompt styles βββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "prompts": | |
| sys_msg(BOLD, "Available prompt styles:") | |
| for p in PROMPT_STYLES: | |
| marker = " β active" if p == prompt_style else "" | |
| print(f" {theme_colors['user']}β’{RESET} {p}{DIM}{marker}{RESET}") | |
| print() | |
| continue | |
| elif cmd.startswith("prompt "): | |
| style = raw.split()[1].lower() | |
| if style not in PROMPT_STYLES: | |
| sys_msg(RED, f"β Unknown style '{style}'.") | |
| sys_msg(DIM, f" Available: {', '.join(PROMPT_STYLES)}") | |
| else: | |
| prompt_style = style | |
| sys_msg(theme_colors["system"], f"β Prompt style set to '{style}'") | |
| continue | |
| # ββ Mood ββββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "moods": | |
| sys_msg(BOLD, "Available moods:") | |
| for m, desc in MOODS.items(): | |
| marker = " β active" if m == mood else "" | |
| preview = desc[:40] + "β¦" if len(desc) > 40 else (desc or "(no prefix)") | |
| print(f" {theme_colors['ai']}β’{RESET} {m:<10} {DIM}{preview}{RESET}{DIM}{marker}{RESET}") | |
| print() | |
| continue | |
| elif cmd == "mood": | |
| sys_msg(YELLOW, f"Current mood: {mood}") | |
| sys_msg(DIM, "Usage: mood <n> e.g. mood funny") | |
| continue | |
| elif cmd.startswith("mood "): | |
| new_mood = raw.split()[1].lower() | |
| if new_mood not in MOODS: | |
| sys_msg(RED, f"β Unknown mood '{new_mood}'.") | |
| sys_msg(DIM, f" Use 'moods' to see options.") | |
| else: | |
| mood = new_mood | |
| sys_msg(theme_colors["system"], f"β Mood set to '{mood}'") | |
| continue | |
| # ββ Timestamp βββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "timestamp on": | |
| timestamp_on = True | |
| sys_msg(theme_colors["system"], "β Timestamps enabled") | |
| continue | |
| elif cmd == "timestamp off": | |
| timestamp_on = False | |
| sys_msg(theme_colors["system"], "β Timestamps disabled") | |
| continue | |
| elif cmd == "timestamp": | |
| state = "on" if timestamp_on else "off" | |
| sys_msg(DIM, f"Timestamps are currently {state}. Use 'timestamp on/off' to toggle.") | |
| continue | |
| # ββ Banner commands βββββββββββββββββββββββββββββββββββ | |
| elif cmd == "banner": | |
| print_banner(ai_name, theme_colors) | |
| continue | |
| elif cmd == "banner off": | |
| banner_enabled = False | |
| sys_msg(DIM, "Banner disabled") | |
| continue | |
| elif cmd == "banner on": | |
| banner_enabled = True | |
| print_banner(ai_name, theme_colors) | |
| continue | |
| # ββ Reset UI ββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "resetui": | |
| ai_name = "AI" | |
| theme = "default" | |
| theme_colors = THEMES[theme].copy() | |
| prompt_style = "classic" | |
| banner_enabled = True | |
| mood = "default" | |
| timestamp_on = False | |
| last_input = "" | |
| provider = DEFAULT_PROVIDER | |
| sys_msg(theme_colors["system"], "β CLI settings reset to defaults") | |
| continue | |
| # ββ Whoami ββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "whoami": | |
| prov_info = PROVIDERS[provider] | |
| prov_col = PROVIDER_COLORS[provider] | |
| divider(theme_colors["system"]) | |
| print(f""" | |
| {DIM}AI Name {RESET}{BOLD}{theme_colors['ai']}{ai_name}{RESET} | |
| {DIM}Provider {RESET}{prov_col}{BOLD}{prov_info['icon']} {prov_info['label']}{RESET} {DIM}{prov_info['tagline']}{RESET} | |
| {DIM}Theme {RESET}{theme} | |
| {DIM}Prompt {RESET}{prompt_style} | |
| {DIM}Mood {RESET}{mood} | |
| {DIM}Timestamp {RESET}{'on' if timestamp_on else 'off'} | |
| {DIM}Banner {RESET}{'enabled' if banner_enabled else 'disabled'} | |
| {DIM}Version {RESET}v{VERSION} | |
| """) | |
| divider(theme_colors["system"]) | |
| print() | |
| continue | |
| # ββ Tips ββββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "tips": | |
| tips = [ | |
| "Use 'providers' to see all AI options with recommendations", | |
| "Type 'provider mini' for coding help, 'provider reasoning' for math", | |
| "Use 'themes' to browse all color themes", | |
| "Use 'color ai green' to fine-tune a single color", | |
| "Try 'mood concise' for shorter AI answers", | |
| "Use 'prompt bracket' for a different input look", | |
| "Type 'help <command>' for details on any command", | |
| "Use 'timestamp on' to see when each reply arrives", | |
| "Reset everything with 'resetui'", | |
| ] | |
| sys_msg(BOLD, "CLI Tips:") | |
| for tip in tips: | |
| print(f" {theme_colors['system']}β’{RESET} {tip}") | |
| print() | |
| continue | |
| # ββ Examples ββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd == "examples": | |
| examples = [ | |
| ("providers", "List all AI providers"), | |
| ("provider mini", "Switch to mini for coding"), | |
| ("provider reasoning", "Switch to reasoning for math"), | |
| ("provider chatgpt", "Switch to ChatGPT for research"), | |
| ("rename Jarvis", "Rename AI to Jarvis"), | |
| ("theme ocean", "Switch to ocean theme"), | |
| ("color user orange", "Make your text orange"), | |
| ("prompt bracket", "Use [you] prompt style"), | |
| ("mood funny", "Get humorous responses"), | |
| ("timestamp on", "Show time on replies"), | |
| ("help provider", "Detailed help on provider"), | |
| ("roast me", "Get roasted π₯"), | |
| ("joke", "Hear a random joke π"), | |
| ] | |
| sys_msg(BOLD, "Example commands:") | |
| for ex, desc in examples: | |
| print(f" {theme_colors['user']}{ex:<26}{RESET} {DIM}{desc}{RESET}") | |
| print() | |
| continue | |
| # ββ Echo ββββββββββββββββββββββββββββββββββββββββββββββ | |
| elif cmd.startswith("echo "): | |
| print(raw[5:]) | |
| continue | |
| # ββ Roast (AI-powered) ββββββββββββββββββββββββββββββββ | |
| elif cmd in ("roast", "roast me") or cmd.startswith("roast "): | |
| if cmd.startswith("roast ") and cmd not in ("roast me",): | |
| target = raw[6:].strip() | |
| else: | |
| target = "you" | |
| sys_msg(DIM, "Cooking up a roast...\n") | |
| roast = get_ai_roast(target) | |
| divider(RED) | |
| print(f"{BOLD}{RED}π₯ [{target}]{RESET} {roast}\n") | |
| continue | |
| # ββ Joke (AI-powered) βββββββββββββββββββββββββββββββββ | |
| elif cmd in ("joke", "jokes"): | |
| sys_msg(DIM, "Fetching a joke...\n") | |
| setup, punchline = get_ai_joke() | |
| divider(theme_colors["system"]) | |
| print(f"{BOLD}{theme_colors['system']}π {setup}{RESET}") | |
| print(f" {punchline}\n") | |
| continue | |
| # ββ History Commands ββββββββββββββββββββββββββββββββββ | |
| elif cmd == "history": | |
| chat.show() | |
| continue | |
| elif cmd == "save txt": | |
| chat.save_txt() | |
| continue | |
| elif cmd == "save json": | |
| chat.save_json() | |
| continue | |
| elif cmd == "metrics": | |
| chat.metrics() | |
| continue | |
| elif cmd == "clearhistory": | |
| chat.clear() | |
| continue | |
| # ββ Unknown single-word commands ββββββββββββββββββββββ | |
| elif cmd in ("color", "prompt", "theme", "mood", "timestamp"): | |
| pass | |
| # ββ AI Handling βββββββββββββββββββββββββββββββββββββββ | |
| valid, err = validate_input(raw, last_input) | |
| if not valid: | |
| print(err) | |
| continue | |
| last_input = raw | |
| # Smart provider suggestion (interactive Y/N) | |
| suggested = detect_suggested_provider(raw) | |
| if suggested and suggested != provider: | |
| sug_info = PROVIDERS[suggested] | |
| sug_col = PROVIDER_COLORS[suggested] | |
| preview = raw[:40] + ("β¦" if len(raw) > 40 else "") | |
| print(f"{YELLOW}π‘ '{preview}' looks like {sug_info['best_for']} β " | |
| f"{sug_col}{BOLD}{sug_info['icon']} {sug_info['label']}{RESET}{YELLOW} " | |
| f"is recommended for better results.{RESET}") | |
| try: | |
| sys.stdout.write(f"{BOLD} Switch to {sug_col}{sug_info['label']}{RESET}{BOLD}? (Y/N): {RESET}") | |
| sys.stdout.flush() | |
| choice = input("").strip().lower() | |
| except (KeyboardInterrupt, EOFError): | |
| choice = "n" | |
| if choice in ("y", "yes"): | |
| provider = suggested | |
| sys_msg(sug_col, f"β Switched to {sug_info['icon']} {BOLD}{sug_info['label']}{RESET}" | |
| f"{sug_col} β {sug_info['tagline']}") | |
| else: | |
| cur_info = PROVIDERS[provider] | |
| cur_col = PROVIDER_COLORS[provider] | |
| sys_msg(DIM, f" Keeping {cur_col}{cur_info['icon']} {cur_info['label']}{RESET}{DIM}.") | |
| print() | |
| chat.add("user", raw) | |
| query = MOODS[mood] + raw | |
| prov_info = PROVIDERS[provider] | |
| prov_col = PROVIDER_COLORS[provider] | |
| sys_msg(DIM, f"Asking {prov_col}{prov_info['icon']} {prov_info['label']}{RESET}{DIM}β¦{RESET}\n") | |
| try: | |
| response = ask_ai(query, provider=provider) | |
| except Exception as e: | |
| response = f"{RED}Error: {e}{RESET}" | |
| time_str = "" | |
| if timestamp_on: | |
| time_str = f"{DIM}[{datetime.now().strftime('%H:%M:%S')}] {RESET}" | |
| print(f"{time_str}{BOLD}{theme_colors['ai']}{ai_name}:{RESET} {response}\n") | |
| chat.add("ai", response) | |
| if __name__ == "__main__": | |
| main() | |