From 28fd685136af20665218a6d8426179bbbd6b4bd6 Mon Sep 17 00:00:00 2001 From: Meleki Date: Fri, 6 Mar 2026 23:02:32 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20vollst=C3=A4ndige=20README=20mit=20Feat?= =?UTF-8?q?ures,=20Installation,=20Konfiguration,=20Deployment=20+=20Scree?= =?UTF-8?q?nshot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++++- Screenshot.png | Bin 0 -> 468599 bytes 2 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 Screenshot.png diff --git a/README.md b/README.md index 0cb0e69..34f8eb7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,163 @@ +![](Screenshot.png) + # // gott -Self-hosted AI-Coding-Agent auf Basis der Anthropic API. Stellt eine Web-UI bereit, über die man in Sessions mit konfigurierbaren Agenten chatten kann. Die Agenten haben Tool-Zugriff (Dateien lesen/schreiben/bearbeiten, Shell-Befehle ausführen) auf ein definiertes Workspace-Verzeichnis. +Self-hosted AI-Coding-Agent auf Basis der Anthropic Claude API. Stellt eine Web-UI bereit, über die man in Sessions mit konfigurierbaren Agenten chatten kann. Die Agenten haben Tool-Zugriff (Dateien lesen/schreiben/bearbeiten, Shell-Befehle ausführen, Suchen, Todos, Memory) auf ein definiertes Workspace-Verzeichnis. -- **Stack:** .NET 10, SQLite, Vanilla-Frontend -- **Port:** 19000 (konfigurierbar via `appsettings.json`) +--- + +## Features + +- **Multi-Agent-System** – Mehrere Agenten mit eigenen Identitäten, Persönlichkeiten und Tool-Konfigurationen +- **Multi-Agent Review** – Optionaler Review-Agent pro Session, der die Arbeit des Worker-Agents prüft und Feedback gibt +- **12 Tools** – `read_file`, `write_file`, `edit_file`, `list_files`, `delete_file`, `execute`, `glob`, `grep`, `todo_write`, `memory_read`, `memory_write`, `memory_list` +- **Streaming** – Echtzeit-Antworten via Server-Sent Events (SSE) +- **Stop-Button** – Laufende Requests zuverlässig abbrechen (inkl. laufender Shell-Befehle) +- **Skill-System** – Wiederverwendbare Prompt-Bausteine, zuordenbar pro Agent +- **Memory** – Persistentes Gedächtnis pro Agent (Dateisystem-basiert) +- **Workspace-Browser** – Integrierter Datei-Explorer mit Editor +- **Telegram-Integration** – Agents über Telegram ansprechen +- **Cronjobs** – Zeitgesteuerte Prompts mit Cron-Expressions +- **Bildgenerierung** – Krea AI Integration für Bildgenerierung +- **Nachrichten teilen** – Einzelne Nachrichten per öffentlichem Link teilen +- **Archivierung** – Chat-Verläufe archivieren und durchsuchen +- **Benutzerverwaltung** – Multi-User mit Admin-Rollen, Impersonate, Login-Links +- **Servereinstellungen** – Admin-UI für `appsettings.Production.json` +- **Desktop-Client** – WPF + WebView2 Windows-App +- **Dark Theme** – CSS-Variables basiertes Theming + +## Tech-Stack + +| Bereich | Technologie | +|---------|-------------| +| **Backend** | .NET 10 Minimal API, C# | +| **Datenbank** | SQLite + Dapper | +| **AI** | Anthropic Claude API (SSE Streaming) | +| **Frontend** | Vanilla JS SPA (kein Framework), CSS Variables | +| **Mathe** | KaTeX (LaTeX-Rendering) | +| **Bilder** | SixLabors.ImageSharp | +| **Cron** | Cronos | +| **Desktop** | WPF + WebView2 (optional) | + +--- + +## Voraussetzungen + +- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) +- Anthropic API Key ([console.anthropic.com](https://console.anthropic.com/)) +- Linux/macOS/Windows (Produktiv: Linux empfohlen) + +## Installation + +```bash +git clone /app/gott +cd /app/gott +dotnet publish -c Release -o publish +``` + +## Konfiguration + +Beim ersten Start wird `appsettings.json` mit Standardwerten verwendet. Für eigene Einstellungen eine `appsettings.Production.json` im `publish/`-Verzeichnis anlegen (oder über die Admin-UI verwalten): + +```json +{ + "Port": 19000, + "AnthropicToken": "sk-ant-...", + "MaxIterations": 100, + "PayloadLogging": false, + "PayloadLoggingGzip": false, + "KreaToken": "", + "KreaApiKey": "" +} +``` + +| Setting | Beschreibung | Default | +|---------|-------------|---------| +| `Port` | Server-Port (erfordert Neustart) | `19000` | +| `AnthropicToken` | Anthropic API Key (**erforderlich**) | – | +| `MaxIterations` | Max Tool-Loop Iterationen pro Chat | `100` | +| `PayloadLogging` | API-Payloads in Konsole loggen | `false` | +| `PayloadLoggingGzip` | Payload-Logs als .gz komprimieren | `false` | +| `KreaToken` | Krea AI API Token (optional) | – | +| `KreaApiKey` | API-Key für Krea-Endpoints ohne User-Auth (optional) | – | + +> **Hinweis:** Die `appsettings.Production.json` wird **nicht** beim `dotnet publish` mitkopiert. Sie lebt ausschließlich im `publish/`-Verzeichnis und kann über die Admin-UI (`#/server-settings`) verwaltet werden. + +## Starten + +```bash +cd publish +dotnet gott.dll +``` + +Die Web-UI ist dann unter `http://localhost:19000` erreichbar. + +Beim ersten Start wird automatisch ein Admin-Account erstellt – die Zugangsdaten werden in der Konsole ausgegeben. + +## Deployment (Linux) + +Empfohlen als systemd-Service hinter einem Reverse Proxy (Nginx, Caddy): + +```bash +# deploy.sh (Beispiel) +#!/bin/bash +set -e +cd /app/gott +systemctl stop gott +dotnet publish -c Release -o publish +systemctl start gott +``` + +Beispiel systemd-Unit (`/etc/systemd/system/gott.service`): + +```ini +[Unit] +Description=gott AI Agent +After=network.target + +[Service] +WorkingDirectory=/app/gott/publish +ExecStart=/usr/bin/dotnet /app/gott/publish/gott.dll +Restart=always +RestartSec=5 +Environment=ASPNETCORE_ENVIRONMENT=Production +Environment=DOTNET_ENVIRONMENT=Production + +[Install] +WantedBy=multi-user.target +``` + +--- + +## Projektstruktur + +``` +gott/ +├── Program.cs # Startup, DI, Middleware, Routing +├── appsettings.json # Standard-Konfiguration +├── Models/ # Records (reine Daten) +├── Services/ # Business-Logic (Singletons) +├── Endpoints/ # HTTP API (Minimal API) +├── Middleware/ # Auth-Middleware +├── Templates/ # Vorlagen (IDENTITY.md, SOUL.md, USER.md) +├── wwwroot/ # Frontend (Vanilla JS SPA) +│ ├── js/ # App, Router, API, Chat, UI-Helpers, Pages +│ └── css/ # Styles (theme.css = CSS Variables) +├── WindowsApp/ # WPF + WebView2 Desktop Client +└── publish/ # Build-Output + Laufzeit-Daten + └── data/ + ├── gott.db # SQLite Datenbank + ├── agents/{id}/ # Memory-Dateien pro Agent + ├── skills/{slug}/ # Skill-Dateien + └── images/ # Krea-generierte Bilder +``` + +> ⚠️ Quelldateien in `wwwroot/` editieren – **niemals** in `publish/wwwroot/`! + +Ausführliche Dokumentation: [ARCHITECTURE.md](ARCHITECTURE.md) · Kurzreferenz: [ARCHITECTURE_QUICK.md](ARCHITECTURE_QUICK.md) + +--- + +## Lizenz + +Proprietär – Alle Rechte vorbehalten. diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..61b6374d804fe4d026b5794ecb47d54b566ee14c GIT binary patch literal 468599 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