338 lines
11 KiB
Markdown
338 lines
11 KiB
Markdown
# 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 <repo-url> 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) → `<projekt>/.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 `/<name> [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/<id>.jsonl` (automatisch von Git ausgeschlossen via
|
|
`.git/info/exclude`).
|
|
|
|
```bash
|
|
nub sessions # Sessions im aktuellen Verzeichnis auflisten
|
|
nub resume <session-id> # 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/<session-id>.jsonl | jq -r 'select(.node) | .node.id + " " + (.node.message.content[0].text // "" | .[0:60])'
|
|
nub resume <session-id> --branch <node-id>
|
|
```
|
|
|
|
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 <session-id> # 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/<name>/SKILL.md` (projektlokal) oder
|
|
`~/.nub/skills/<name>/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:** `/<name> [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
|
|
`<server>__`-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 <session-id> --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-id> Session fortsetzen
|
|
nub resume <session-id> --branch <node-id>
|
|
von einem früheren Knoten fortsetzen (Rewind)
|
|
nub sessions Sessions auflisten
|
|
nub map REPOMAP.md erzeugen/aktualisieren
|
|
nub context geladenen Kontext anzeigen
|
|
nub compact <session-id> Compaction erzwingen
|
|
nub skills gefundene Skills auflisten
|
|
nub mcp MCP-Server verbinden, Tools auflisten
|
|
nub [resume ...] --profile <p> nur MCP-Server aus profiles.<p> 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).
|