toolbox: interaktiver Tool-Launcher mit fzf + iTerm2/Linux-Support
- fzf-basierte Auswahl mit Kategorien und Beschreibungen - TUI-Tools öffnen in neuem Tab (iTerm2 auf Mac, xfce4-terminal/gnome-terminal auf Linux) - Output-Tools laufen inline - Mac-only Tools (temps) werden auf Linux ausgeblendet - Linux-Binaries (batcat, fdfind) werden automatisch aufgelöst - tools() → toolbox, tools-ref() behält statische Cheatsheet-Ansicht Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f3a8571057
commit
978065289d
2 changed files with 210 additions and 2 deletions
203
bin/toolbox
Executable file
203
bin/toolbox
Executable file
|
|
@ -0,0 +1,203 @@
|
|||
#!/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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue