#!/usr/bin/env python3 """ toolbox — interaktiver Terminal-Tool-Launcher Läuft auf macOS (iTerm2) und Linux (Xubuntu/xfce4-terminal, gnome-terminal, …) """ import subprocess import shutil import sys import os import platform from dataclasses import dataclass, field IS_MAC = platform.system() == "Darwin" IS_LINUX = platform.system() == "Linux" @dataclass class Tool: category: str name: str description: str command: str binary: str # Binary für Verfügbarkeitscheck new_tab: bool = True # TUI → neuer Tab, Output-Tools → inline mac: bool = True # auf macOS anzeigen linux: bool = True # auf Linux anzeigen TOOLS = [ # --- Git --- Tool("Git", "lazygit", "Git TUI: stagen, committen, pushen, rebasen", "lazygit", "lazygit"), Tool("Git", "gitcheck", "Status aller Repos prüfen", "~/git-check-all.sh", "git", new_tab=False), Tool("Git", "gitsync", "Alle Repos mit Gitea synchronisieren", "~/git-projekte/dotfiles-rene/bin/git-sync-all.sh", "git", new_tab=False), Tool("Git", "git log", "Commit-History (mit delta, side-by-side)", "git log -p", "git", new_tab=False), Tool("Git", "git diff", "Änderungen anzeigen (mit delta)", "git diff", "git", new_tab=False), # --- Navigation & Dateien --- Tool("Dateien", "yazi", "Terminal-Dateimanager (q = quit)", "yazi", "yazi"), Tool("Dateien", "fzf", "Fuzzy-Finder (Ctrl+T Dateien, Ctrl+R History)", "fzf", "fzf"), Tool("Dateien", "ncdu", "Interaktive Festplattennutzung", "ncdu ~", "ncdu"), Tool("Dateien", "duf", "Übersicht freier Speicherplatz", "duf", "duf", new_tab=False), Tool("Dateien", "fd", "Schnelles find (Beispiel: fd .py)", "fd", "fd", new_tab=False), Tool("Dateien", "rg", "Blitzschnelles grep (Beispiel: rg TODO)", "rg", "rg", new_tab=False), # --- Anzeige --- Tool("Anzeige", "bat", "cat mit Syntax-Highlighting", "bat", "bat", new_tab=False), Tool("Anzeige", "eza -la", "Modernes ls mit Details, Farben, Git-Status", "eza -la", "eza", new_tab=False), Tool("Anzeige", "eza -T", "Verzeichnisbaum", "eza -T", "eza", new_tab=False), Tool("Anzeige", "delta", "Schöne Git-Diffs (wird automatisch verwendet)", "git diff HEAD~1 | delta", "delta", new_tab=False), # --- System & Monitoring --- Tool("System", "btop", "Systemmonitor (CPU/RAM/Netz/Prozesse)", "btop", "btop"), Tool("System", "fastfetch", "Systeminfo Übersicht", "fastfetch", "fastfetch", new_tab=False), Tool("System", "temps", "CPU/GPU-Temperatur + Akku (Mac)", "sudo powermetrics -s cpu_power,gpu_power,thermal,battery -i 1000 -n 1", "sudo", new_tab=False, linux=False), # --- Netzwerk --- Tool("Netzwerk", "nmap", "Netzwerk-Scanner (Beispiel: nmap 10.47.11.0/24)", "nmap -sn 10.47.11.0/24", "nmap", new_tab=False), # --- Berechnung --- Tool("Rechnen", "units", "Einheitenumrechnung (Beispiel: units '1 kWh' MJ)", "units", "units"), Tool("Rechnen", "python3", "Python REPL für schnelle Berechnungen", "python3", "python3"), # --- KI --- Tool("KI", "ki-chat", "Offline-KI interaktiver Chat", "ki interactive", "ki"), Tool("KI", "ki-agent", "Offline-KI Agent-Modus (Datei-/Shell-Zugriff)", "ki interactive --agent-mode", "ki"), Tool("KI", "ki-commit", "Commit-Message mit KI generieren", "ki commit", "ki", new_tab=False), Tool("KI", "ki-diff", "Git-Diff mit KI erklären lassen", "ki diff", "ki", new_tab=False), # --- Screensaver / Spass --- Tool("Spass", "cmatrix", "Matrix-Regen (q = quit)", "cmatrix -sab", "cmatrix"), Tool("Spass", "asciiquarium", "Aquarium im Terminal (q = quit)", "asciiquarium", "asciiquarium"), Tool("Spass", "pipes.sh", "Animierte Rohre (q = quit)", "pipes.sh -t 0 -p 4", "pipes.sh"), Tool("Spass", "cbonsai", "Wachsender Bonsai-Baum (q = quit)", "cbonsai -l", "cbonsai"), Tool("Spass", "nms", "Sneakers-Entschlüsselungseffekt", "ls -la | nms", "nms", new_tab=False), ] # --- Binaries die auf Linux anders heissen --- LINUX_ALIASES = { "fd": "fdfind", "bat": "batcat", } def is_available(tool: Tool) -> bool: if IS_MAC and not tool.mac: return False if IS_LINUX and not tool.linux: return False binary = tool.binary if IS_LINUX: binary = LINUX_ALIASES.get(binary, binary) return shutil.which(binary) is not None def resolve_command(command: str) -> str: """Passt Befehle für Linux an (bat→batcat etc.).""" if IS_LINUX: for mac_bin, linux_bin in LINUX_ALIASES.items(): if command.startswith(mac_bin + " ") or command == mac_bin: command = linux_bin + command[len(mac_bin):] return command def detect_linux_terminal() -> list[str]: """Findet einen verfügbaren Terminal-Emulator auf Linux.""" candidates = [ (["xfce4-terminal", "--tab", "-e"], "xfce4-terminal"), (["gnome-terminal", "--tab", "--"], "gnome-terminal"), (["kitty", "bash", "-c"], "kitty"), (["alacritty", "-e"], "alacritty"), (["xterm", "-e"], "xterm"), ] for args, binary in candidates: if shutil.which(binary): return args return [] def open_new_tab(command: str): """Öffnet Befehl in neuem Tab — macOS oder Linux.""" if IS_MAC: script = f''' tell application "iTerm2" activate tell current window create tab with default profile tell current session write text "{command}" end tell end tell end tell ''' subprocess.run(["osascript", "-e", script], capture_output=True) else: terminal_args = detect_linux_terminal() if terminal_args: subprocess.Popen(terminal_args + [f"bash -c '{command}; exec bash'"]) else: # Fallback: inline os.system(command) def run_inline(command: str): os.system(command) def main(): available = [t for t in TOOLS if is_available(t)] if not shutil.which("fzf"): print("fzf nicht gefunden – bitte installieren:") print(" macOS: brew install fzf") print(" Linux: sudo apt install fzf") sys.exit(1) fzf_lines = [ f"{t.category:<12} {t.name:<20} {t.description}" for t in available ] result = subprocess.run( [ "fzf", "--ansi", "--height=70%", "--layout=reverse", "--border=rounded", "--prompt= Tools > ", "--header= Kategorie Name Beschreibung\n ─────────────────────────────────────────────────────────────", "--color=header:italic:cyan,prompt:green,pointer:green", ], input="\n".join(fzf_lines), text=True, capture_output=True, ) if result.returncode != 0 or not result.stdout.strip(): sys.exit(0) chosen_line = result.stdout.strip() chosen_tool = None for i, line in enumerate(fzf_lines): if line == chosen_line: chosen_tool = available[i] break if not chosen_tool: sys.exit(0) command = resolve_command(chosen_tool.command) print(f"\n▶ {chosen_tool.name} — {command}\n") if chosen_tool.new_tab: open_new_tab(command) else: run_inline(command) if __name__ == "__main__": main()