# nub Schlanker, terminal-nativer Coding-Agent als einzelnes Go-Binary. Keine Laufzeit-Abhängigkeiten, interaktive TUI, skriptbarer Print-Modus. - Features: [docs/FEATURES.md](docs/FEATURES.md) - Architektur/Design: [docs/KONZEPT.md](docs/KONZEPT.md) - Entwicklung/Tests: [docs/TESTING.md](docs/TESTING.md) ## Voraussetzungen - Go 1.24+ - OpenAI-kompatibler Endpoint (OpenAI-API-Key oder lokaler Server: Ollama, LM Studio, vLLM, …) - Linux/macOS (Windows nur über WSL) ## Installation ```bash git clone nub && cd nub go build -o nub ./cmd/nub mv nub /usr/local/bin/ # optional ``` ## Schnellstart ```bash export OPENAI_API_KEY=sk-... cd /pfad/zu/deinem/projekt nub ``` Startet eine interaktive Session im aktuellen Verzeichnis — das wird zur Wurzel für alle Datei-Tools und `bash`-Aufrufe. Ohne Config-Datei laufen sinnvolle Defaults (Modell `gpt-4o` gegen die echte OpenAI-API). ## Konfiguration Geschichtet, spätere Schicht gewinnt: `Defaults` → `~/.config/nub/config.toml` (global) → `/.nub/config.toml` (projektlokal) → `NUB_*`-Env-Vars. ### Nur Umgebungsvariablen | Variable | Pflicht | Default | Bedeutung | |---|---|---|---| | `NUB_API_KEY` | nein* | — | API-Key, Vorrang vor `OPENAI_API_KEY` | | `OPENAI_API_KEY` | nein* | — | Fallback | | `NUB_BASE_URL` | nein | `https://api.openai.com/v1` | Endpoint-URL | | `NUB_MODEL` | nein | `gpt-4o` | Modellname | \* Pflicht, außer der Endpoint beginnt mit `http://` (lokal, kein Key nötig). ```bash export OPENAI_API_KEY=sk-... export NUB_MODEL=gpt-4o-mini ``` ```bash export NUB_BASE_URL=http://localhost:11434/v1 export NUB_MODEL=qwen2.5-coder:7b ``` ### Mit Config-Datei `nub init` (oder `/init` in der TUI) legt `.nub/config.toml` (kommentierte Beispiel-Config inkl. Permissions) und ein minimales `AGENTS.md` an — nur falls noch nicht vorhanden. Von Hand, `.nub/config.toml` (`version = 1` ist Pflicht): ```toml version = 1 [model] default = "gpt-4o-mini" endpoint = "openai" [[endpoint]] name = "openai" base_url = "https://api.openai.com/v1" api_key = "env:OPENAI_API_KEY" # oder direkt der Key, oder "none" für lokal [context] files = ["AGENTS.md", "REPOMAP.md"] walk_up = true max_tokens = 20000 ``` - Mehrere `[[endpoint]]`-Blöcke möglich; `model.endpoint` wählt aus. - `[endpoint.caps]` überschreibt einzelne Fähigkeiten (z.B. `parallel_tool_calls = false` für Server, die das nicht unterstützen). - Fehlt bei einem Nicht-lokalen Endpoint der Key, bricht `nub` sofort ab. ## Bedienung TUI standardmäßig im Terminal (stdin+stdout), sonst Print-Modus. `-p` erzwingt Print-Modus. ### TUI | Taste/Kommando | Wirkung | |---|---| | `Enter` | Nachricht senden (läuft ein Turn: als Steering zwischen zwei Tool-Aufrufen eingeschleust) | | `Alt+Enter` | als Follow-up queuen (erst nach Ende des laufenden Turns) | | `↑`/`↓` | Eingabe-Historie (bei leerer Eingabezeile) | | `PgUp`/`PgDn`, `Ctrl+U`/`Ctrl+D`, Mausrad | Nachrichtenliste scrollen | | `Ctrl+C`, `/exit`, `/quit` | beenden | Diffs (`write`/`edit`) farbig im Transkript, fertige Antworten als Markdown. Farben passen sich automatisch an helles/dunkles Terminal-Theme an. **Slash-Kommandos:** | Kommando | Wirkung | |---|---| | `/model [name]` | Modell anzeigen oder für den Rest der Session wechseln | | `/tree`, `/branch` | Session-Baum anzeigen, Rewind mit ↑/↓ + Enter | | `/sessions` | Sessions auflisten, wechseln (Enter), löschen (Entf, Rückfrage) | | `/compact` | Session sofort zusammenfassen | | `/context` | geladenen Kontext mit Herkunft/Tokenkosten anzeigen | | `/mcp` | verbundene MCP-Server und ihre Tools | | `/skills` | gefundene Skills | | `/export [pfad]` | Transkript als Markdown exportieren | | `/init` | `.nub/config.toml` + `AGENTS.md` anlegen, falls nicht vorhanden | | `/clear` | Anzeige leeren (Session bleibt erhalten) | | `/exit`, `/quit` | beenden | | `/help` | Kommando-Übersicht | Jeder gefundene Skill ist zusätzlich direkt als `/ [Zusatzinfo]` aufrufbar (siehe [Skills](#skills)). ### Print-Modus (`-p`) Zeilenweise stdin = eine Nachricht. Antworten auf stdout, Tool-Aktivität (`→ tool_name {...}`, `← tool_name [ok|error]`, `bash`-Output) auf stderr. `Ctrl+D` (EOF) beendet sauber nach dem aktuellen Turn. ```bash ./nub -p Lies die Datei go.mod und fasse in einem Satz zusammen, was das Modul ist. ``` Beim Start gibt `nub` (beide Modi) auf stderr die Session-ID aus (`session: 01J...`). ## Sessions Jeder Lauf ohne Subcommand startet eine neue Session, persistiert unter `.nub/sessions/.jsonl` (automatisch von Git ausgeschlossen via `.git/info/exclude`). ```bash nub sessions # Sessions im aktuellen Verzeichnis auflisten nub resume # Session fortsetzen ``` In der TUI: `/sessions` (Liste, wechseln, löschen). **Rewind/Branch:** in der TUI über `/tree`/`/branch`. Über CLI/Print-Modus per Node-ID: ```bash cat .nub/sessions/.jsonl | jq -r 'select(.node) | .node.id + " " + (.node.message.content[0].text // "" | .[0:60])' nub resume --branch ``` Setzt den Head auf den gewählten Knoten zurück; eine neue Nachricht bildet dort einen zweiten Ast. Der ursprüngliche Ast bleibt erhalten. ## Kontext (AGENTS.md, REPOMAP.md, nub map) System-Prompt = Basis-Prompt (überschreibbar durch `SYSTEM.md` im Projekt-Root) + statische Umgebungsinfo (OS/Shell/Repo-Root/Branch) + Dateien aus `context.files` (Default: `AGENTS.md`, `REPOMAP.md`). Mit `walk_up = true` (Default) werden Treffer zwischen aktuellem Verzeichnis und Projekt-Root eingesammelt. ```bash nub context # geladene Kontext-Dateien mit Herkunft und Tokenkosten nub map # REPOMAP.md generieren/aktualisieren ``` `context.max_tokens` (Default 20000) begrenzt Kontext-Dateien, kürzt sichtbar statt still. `nub map` erzeugt Verzeichnisbaum + exportierte Symbole (`.go` über `go/ast`, sonst erste Zeile als Fallback); warnt, wenn die Repomap vom Git-Stand abweicht. ## Compaction & todo-Tool Vor jedem Turn geschätzte Tokenkosten; über Schwelle (Default 75 % des Modell-Fensters) wird automatisch komprimiert: ältere Hälfte der Session wird per separatem Prompt zusammengefasst, letzte Turns bleiben im Original. Original-Nodes bleiben erhalten — Rewind vor die Compaction funktioniert weiterhin. ```bash nub compact # Compaction sofort erzwingen ``` `todo`-Tool: In-Memory-Todo-Liste für lange Aufgaben (nicht persistiert), das Modell entscheidet selbst über die Nutzung. ## Skills `.nub/skills//SKILL.md` (projektlokal) oder `~/.nub/skills//SKILL.md` (global, konfigurierbar über `[skills] paths = [...]`). Projektlokal überschreibt global. ```markdown --- name: refactoring description: Vorgehen für größere Refactorings in diesem Repo. --- # Refactoring ...Body... ``` Beim Start landet nur `name`+`description` im System-Prompt (~30 Token pro Skill); den Body lädt das Modell selbst nach (`read_skill`), weitere Dateien im Skill-Verzeichnis über `read`/`glob`. ```bash nub skills # Index-Vorschau ``` **Als Slash-Command:** `/ [Zusatzinfo]`, z.B. `/refactoring fokus auf error handling` — schickt eine kurze Direktive statt Rohdaten, Progressive Disclosure bleibt erhalten. Eingebaute Kommandos haben bei Namenskollision Vorrang. ## MCP ```toml [[mcp]] name = "github" command = "gh-mcp-server" args = [] tools = ["create_issue", "get_pull_request"] # Allowlist; leer = alle [[mcp]] name = "linear" url = "https://mcp.linear.app/mcp" ``` Alle Server parallel verbunden (Timeout 5s/Server), Tools mit `__`-Präfix (z.B. `github__create_issue`). Nicht erreichbare Server verhindern den Start nicht (Warnung, `nub` läuft weiter). ```bash nub mcp # verbindet und listet Tools ``` **Profile** grenzen ein, welche Server pro Aufruf verbunden werden: ```toml [profiles] review = ["github"] default = [] ``` ```bash nub --profile review nub resume --profile review ``` Ohne `[profiles]`-Sektion: immer alle konfigurierten Server. ## Permissions ```toml [permissions] read = "auto" glob = "auto" grep = "auto" write = "ask" edit = "ask" bash = "ask" deny_paths = [".git/**", "**/.env", "**/id_rsa*"] deny_bash = ["rm -rf /", "git push --force*"] ``` | Modus | Verhalten | |---|---| | `auto` (Default) | läuft ohne Rückfrage | | `ask` | TUI: Rückfrage-Block im Transkript, `y`/`Y` erlaubt, jede andere Taste lehnt ab. Print-Modus: harter Fehler | | `deny` | läuft nie | - `deny_paths`/`deny_bash` überstimmen `auto`/`ask` immer. `deny_paths` gilt für `read`/`write`/`edit`/`grep` (`path`-Feld), `deny_bash` matcht den Befehlstext (Glob/Teilstring). - Tools ohne eigenes Config-Feld (MCP, `todo`, `read_skill`) laufen immer `auto`. - Mehrere gleichzeitige Tool-Aufrufe: Rückfragen laufen nacheinander vor dem parallelen Start der erlaubten Calls — nie zwei Dialoge gleichzeitig. ### `question`-Tool Gleiche Rückfrage-Mechanik als eigenständiges Tool: Modell oder Skill-Anweisung stellt eine freie Frage (optional mit Vorschlägen), Antwort kommt als Tool-Ergebnis zurück. TUI: Block im Transkript, nächste Eingabe zählt als Antwort statt neuer Nachricht/Kommando. Print-Modus: sofortiger Fehler (kein Blockieren, Print läuft headless). ## Alle CLI-Kommandos ``` nub init .nub/config.toml + AGENTS.md anlegen, falls nicht vorhanden nub neue Session starten (TUI im Terminal) nub -p Print-Modus erzwingen nub resume Session fortsetzen nub resume --branch von einem früheren Knoten fortsetzen (Rewind) nub sessions Sessions auflisten nub map REPOMAP.md erzeugen/aktualisieren nub context geladenen Kontext anzeigen nub compact Compaction erzwingen nub skills gefundene Skills auflisten nub mcp MCP-Server verbinden, Tools auflisten nub [resume ...] --profile

nur MCP-Server aus profiles.

verbinden ``` ## Bekannte Grenzen - Bilder werden nicht verarbeitet, auch wenn der Endpoint sie unterstützt. - Permissions/`deny_paths` gelten nur für die sechs eingebauten Datei-/ Bash-Tools — MCP-Tools laufen immer `auto`. - Compaction nutzt immer dasselbe Modell wie die Session. - MCP-Server reconnecten nicht zur Laufzeit (Neustart nötig); nur Tools angebunden, keine Prompts/Resources. - Skills müssen von Hand angelegt werden. Ausführlichere Liste: [docs/TESTING.md](docs/TESTING.md). ## Mitentwickeln Build, Tests, manuelle Testrezepte: [docs/TESTING.md](docs/TESTING.md). Architektur/Design-Entscheidungen: [docs/KONZEPT.md](docs/KONZEPT.md).