commit 92ec59e6e0c9ca1a3d6c531b449731f901f701be Author: marcus.hinz Date: Thu Jul 9 09:21:43 2026 +0200 Initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d6fab71 Binary files /dev/null and b/.DS_Store differ diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..00cdcdb --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,24 @@ +{ + "autoMemoryDirectory": "~/claude-projects/ai-control/memory", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/ai-control/OFFENE-PUNKTE.md", + "type": "command" + } + ] + } + ] + }, + "permissions": { + "additionalDirectories": [ + "~/projects/ai-control" + ], + "allow": [ + "Edit(~/projects/ai-control/**)", + "Edit(~/claude-projects/ai-control/**)" + ] + } +} diff --git a/.claude/skills/git-commit/SKILL.md b/.claude/skills/git-commit/SKILL.md new file mode 100644 index 0000000..b95ed83 --- /dev/null +++ b/.claude/skills/git-commit/SKILL.md @@ -0,0 +1,85 @@ +--- +name: git-commit +description: > + Commit + Push des ai-control-App-Repos auf lintus (origin, volle Dev-Historie). + Optional, nur auf ausdrückliche Ansage: kuratierter Squash-Publish + Tag auf + github und GitHub-Release mit Linux-Binaries. Gedacht für die Linux-Box. +--- + +# git-commit — ai-control veröffentlichen + +Zwei Ebenen, strikt getrennt: + +1. **Immer**: Commit + Push auf **lintus** (`origin`) — die volle Dev-Historie. +2. **Optional, nur wenn ausdrücklich verlangt**: Publish auf **github** (Squash-Mirror) und ein GitHub-Release mit Linux-Binaries. + +Nie ohne Ansage die github-Ebene mitlaufen lassen. Commit-Messages ohne jeden Claude-Hinweis (kein Co-Authored-By, kein „Generated with"). + +## Repo & Remotes + +- App-Repo: `~/projects/ai-control` +- `origin` → `ssh://git@git.lintus.art:2222/marcus.hinz/ai-control.git` — volle Historie, hierher geht jeder Commit. +- `github` → `https://github.com/mHinz-hub/ai-control.git` — **kuratierter Squash-Mirror**: pro Release genau ein Commit `ai-control X.Y.Z`, dessen Tree dem Dev-Tree entspricht. Die volle Dev-Historie gehört **nicht** dorthin. + +`~/claude-projects/ai-control` (Control-Repo) ist ein anderes Repo (nur lintus) — nicht gemeint. + +## 1. Normaler Commit + Push (immer) + +```bash +cd ~/projects/ai-control +git add -A +git status -s # prüfen, was reingeht +git commit -m "" +git push origin main +``` + +## 2. github-Publish (optional) + +Voraussetzung: der Release-Commit liegt auf lokalem `main` und ist auf lintus gepusht; bei einem Versionssprung ist die Version in **allen vier** Dateien gehoben (siehe Abschnitt 4). + +`git push github main` ist verboten (divergiert gegen den Squash-Mirror → Reject). Stattdessen einen einzelnen Squash-Commit mit dem aktuellen Tree auf `github/main` setzen: + +```bash +cd ~/projects/ai-control +git fetch github +tree=$(git rev-parse HEAD^{tree}) +new=$(git commit-tree "$tree" -p github/main -m "ai-control X.Y.Z") +git push github "$new:main" # Squash-Commit als neues github/main +git tag -f vX.Y.Z "$new" # Tag auf den Squash-Commit (nicht den Dev-Commit) +git push github -f vX.Y.Z # bestehenden Tag ggf. ersetzen +``` + +Das hält die lokale Dev-Historie unangetastet und reiht sich in `ai-control 0.1.0 / 0.1.1 / …` ein. Der Tag zeigt bewusst auf den Squash-Commit, nicht auf den Dev-Commit. + +## 3. Release mit Linux-Binaries (optional, auf der Linux-Box) + +Baudeps auf der Box: `webkit2gtk-4.1`, `libgtk-3`, `libayatana-appindicator3`, `librsvg2`, `patchelf`. + +```bash +cd ~/projects/ai-control +npm run tauri build +# Artefakte: +# src-tauri/target/release/bundle/appimage/ai-control_*.AppImage +# src-tauri/target/release/bundle/deb/ai-control_*.deb + +# Release anlegen (falls noch keins zum Tag existiert): +gh release create vX.Y.Z --repo mHinz-hub/ai-control --title "vX.Y.Z" --notes "<Änderungen>" + +# Binaries anhängen (jederzeit nachträglich möglich): +gh release upload vX.Y.Z --repo mHinz-hub/ai-control \ + src-tauri/target/release/bundle/appimage/ai-control_*.AppImage \ + src-tauri/target/release/bundle/deb/ai-control_*.deb +``` + +Der macOS-Build (`./build.sh`, DMG) hängt nicht am Linux-Build; `macos-private-api` u. Ä. sind per `cfg` aus. Die GNOME-Popup-Extension ist separat, kein Teil des Binaries. + +## 4. Version heben (bei Release) + +Vor Abschnitt 2/3 die Version in vier Dateien gleichziehen: + +- `package.json` → `"version"` +- `src-tauri/Cargo.toml` → `[package] version` +- `src-tauri/Cargo.lock` → Eintrag unter `name = "ai-control"` +- `src-tauri/tauri.conf.json` → `"version"` + +Dann normal committen (Abschnitt 1), danach optional 2/3. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/OFFENE-PUNKTE.md b/OFFENE-PUNKTE.md new file mode 100644 index 0000000..644776f --- /dev/null +++ b/OFFENE-PUNKTE.md @@ -0,0 +1 @@ +# Offene Punkte — bei jedem Start prüfen und abhaken diff --git a/Screenshot 2026-07-05 at 14.32.05.png b/Screenshot 2026-07-05 at 14.32.05.png new file mode 100644 index 0000000..a0b7e5c Binary files /dev/null and b/Screenshot 2026-07-05 at 14.32.05.png differ diff --git a/Screenshot 2026-07-05 at 14.37.25.png b/Screenshot 2026-07-05 at 14.37.25.png new file mode 100644 index 0000000..bd23631 Binary files /dev/null and b/Screenshot 2026-07-05 at 14.37.25.png differ diff --git a/Screenshot 2026-07-05 at 14.37.55.png b/Screenshot 2026-07-05 at 14.37.55.png new file mode 100644 index 0000000..80b95a7 Binary files /dev/null and b/Screenshot 2026-07-05 at 14.37.55.png differ diff --git a/Screenshot 2026-07-05 at 14.38.13.png b/Screenshot 2026-07-05 at 14.38.13.png new file mode 100644 index 0000000..3b74b3e Binary files /dev/null and b/Screenshot 2026-07-05 at 14.38.13.png differ diff --git a/Screenshot 2026-07-05 at 14.38.27.png b/Screenshot 2026-07-05 at 14.38.27.png new file mode 100644 index 0000000..f46b31d Binary files /dev/null and b/Screenshot 2026-07-05 at 14.38.27.png differ diff --git a/Screenshot 2026-07-05 at 14.39.04.png b/Screenshot 2026-07-05 at 14.39.04.png new file mode 100644 index 0000000..5902262 Binary files /dev/null and b/Screenshot 2026-07-05 at 14.39.04.png differ diff --git a/ai-control.json b/ai-control.json new file mode 100644 index 0000000..c72185b --- /dev/null +++ b/ai-control.json @@ -0,0 +1,7 @@ +{ + "pool": "private", + "terminal": { + "theme": "monokai", + "icon": "ai-control.png" + } +} diff --git a/bin/connect-test b/bin/connect-test new file mode 100755 index 0000000..0c5b031 --- /dev/null +++ b/bin/connect-test @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# connect-test — prüft, ob claude mit der aktuellen Env authentifizieren und antworten kann. +# Exit 0 = Verbindung ok. Für Negativtest mit ungültigem Key: +# ANTHROPIC_API_KEY=sk-ant-ungueltig connect-test +set -euo pipefail + +"${CLAUDE_BIN:-$HOME/.local/bin/claude}" -p 'Antworte nur mit: OK' --model claude-sonnet-5 diff --git a/bin/kc-backup b/bin/kc-backup new file mode 100755 index 0000000..45c21b2 --- /dev/null +++ b/bin/kc-backup @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# kc-backup — sichert das Secret des Claude-Code-Keychain-Eintrags in eine Datei. +# Ablage außerhalb des Git-Repos (Default: ~/.ssh), damit das Secret nie committet wird. +# Usage: kc-backup [zieldatei] +set -euo pipefail + +dest=${1:-$HOME/.ssh/claude-keychain-backup.json} +security find-generic-password -s "Claude Code-credentials" -a "$USER" -w > "$dest" +chmod 600 "$dest" +echo "Keychain-Secret gesichert nach: $dest ($(wc -c < "$dest" | tr -d ' ') Bytes)" diff --git a/bin/kc-delete b/bin/kc-delete new file mode 100755 index 0000000..909a01c --- /dev/null +++ b/bin/kc-delete @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# kc-delete — löscht den Claude-Code-Keychain-Eintrag. +# Vorher kc-backup ausführen; zurückspielen mit kc-restore. +set -euo pipefail + +security delete-generic-password -s "Claude Code-credentials" -a "$USER" +echo "Keychain-Eintrag 'Claude Code-credentials' gelöscht." diff --git a/bin/kc-restore b/bin/kc-restore new file mode 100755 index 0000000..2ec6c04 --- /dev/null +++ b/bin/kc-restore @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# kc-restore — spielt das mit kc-backup gesicherte Secret in den Keychain zurück. +# -U aktualisiert einen ggf. vorhandenen Eintrag statt zu scheitern. +# Usage: kc-restore [backupdatei] +set -euo pipefail + +src=${1:-$HOME/.ssh/claude-keychain-backup.json} +security add-generic-password -U -s "Claude Code-credentials" -a "$USER" -w "$(cat "$src")" +echo "Keychain-Eintrag aus $src wiederhergestellt." diff --git a/bin/kc-status b/bin/kc-status new file mode 100755 index 0000000..03c2e08 --- /dev/null +++ b/bin/kc-status @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# kc-status — zeigt Zustand des Claude-Code-Keychain-Eintrags und der Auth-Env. +# mdat = letzte Änderung des Eintrags; ändert sich, wenn eine Session den Keychain beschreibt. +set -euo pipefail + +echo "== Keychain-Eintrag 'Claude Code-credentials' ==" +security find-generic-password -s "Claude Code-credentials" -a "$USER" 2>&1 | grep -E '"(mdat|cdat)"' || echo "kein Eintrag vorhanden" + +echo "== Auth-Env dieser Shell ==" +env | grep -E '^(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN|CLAUDE_CONFIG_DIR)=' | sed -E 's/=(.).*/=\1…/' || echo "keine Auth-Env gesetzt" diff --git a/bin/oauth-configdir-test b/bin/oauth-configdir-test new file mode 100755 index 0000000..6ed749d --- /dev/null +++ b/bin/oauth-configdir-test @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# oauth-configdir-test — prüft, ob claude auf macOS die .credentials.json aus +# CLAUDE_CONFIG_DIR liest, wenn kein Keychain-Eintrag existiert. +# Ablauf: Backup → Keychain löschen → claude mit bereinigter Auth-Env → +# Nachkontrollen → Restore. Restore läuft auch bei fehlgeschlagenem Testlauf. +# Usage: oauth-configdir-test [pool] (Default: Mx9Privat) +set -u +cd "$(dirname "$0")/.." + +pool=${1:-Mx9Privat} +pooldir="$HOME/claude-pools/$pool" + +bin/kc-backup || exit 1 +bin/kc-delete || exit 1 + +echo "== Testlauf: Env ohne ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR=$pooldir ==" +env -u ANTHROPIC_API_KEY -u CLAUDE_CODE_OAUTH_TOKEN \ + CLAUDE_CONFIG_DIR="$pooldir" \ + "${CLAUDE_BIN:-$HOME/.local/bin/claude}" -p 'Antworte nur mit: OK' --model claude-sonnet-5 +echo "EXIT: $?" + +echo "== credentials-Datei nach Lauf (#10039: darf nicht gelöscht sein) ==" +ls -la "$pooldir/.credentials.json" + +echo "== Keychain nach Lauf (es darf KEIN Eintrag entstanden sein) ==" +bin/kc-status + +echo "== Restore ==" +bin/kc-restore +rm "$HOME/.ssh/claude-keychain-backup.json" +bin/kc-status diff --git a/icon-aic-512.png b/icon-aic-512.png new file mode 100644 index 0000000..ffc5ae4 Binary files /dev/null and b/icon-aic-512.png differ diff --git a/icon-aic.svg b/icon-aic.svg new file mode 100644 index 0000000..53ea461 --- /dev/null +++ b/icon-aic.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + aIC + diff --git a/memory/MEMORY.md b/memory/MEMORY.md new file mode 100644 index 0000000..6f0724f --- /dev/null +++ b/memory/MEMORY.md @@ -0,0 +1,8 @@ +# Memory Index + +- [Go-Pflicht](feedback-go-pflicht.md) — weitreichende Änderungen nie ohne ausdrückliches Go; eine Rückfrage von Marcus ist kein Go (Vorfall 2026-07-04) +- [Terminal-Tests mit 08-07](feedback-terminal-test-08-07.md) — ABSOLUTES VERBOT: nie ein Terminal für/aus ai-control starten (killt die eigene Session, 3× Kontext verloren am 2026-07-04). Terminal-Tests nur mit 08-07 und nur auf ausdrückliche Ansage + +- [Multi-Account & Keychain](multi-account-keychain.md) — Stand 2.1.199: suffixierter Keychain-Eintrag pro Config-Dir verifiziert; Pools umbenannt, Keychain-Endstand ein Eintrag 096c4ef9 (privateDefault); Kaltstart über Starter läuft (2026-07-04), Qualität laut Marcus noch nicht zufriedenstellend +- [Pool-Setup Stand](ai-control-pool-setup.md) — Layout ~/.config/ai-control (pools, projects.json-Registry, icons, settings.json mit claudeCommand); eingebautes Terminal + Tray mit Statuspunkten; Stand 2026-07-05 GitHub-reif (README en, MIT, Screenshots); Deploy braucht lsregister +- [Linux-Umzug](linux-umzug.md) — ab 2026-07-05 laufen die Sessions auf der Linux-Box; Start per start-claude.sh (Einzeiler, Pool private); macOS-Code muss noch hinter cfg(target_os) diff --git a/memory/ai-control-pool-setup.md b/memory/ai-control-pool-setup.md new file mode 100644 index 0000000..564762f --- /dev/null +++ b/memory/ai-control-pool-setup.md @@ -0,0 +1,41 @@ +--- +name: ai-control-pool-setup +description: "Stand des Pool-Setups seit 2026-07-03 — Layout, Starter, was gelöscht wurde, offene Punkte" +metadata: + node_type: memory + type: project + originSessionId: e108ad75-f176-43b6-901d-c3ec19ec3961 +--- + +Seit 2026-07-03 produktiv: + +- Pools: `~/.config/ai-control/pools//` = CLAUDE_CONFIG_DIR. `pool.json` (name, credentialType), oauth: `.credentials.json`, apikey: `apikey` + `apiKeyHelper` in pool-`settings.json`. Pools (umbenannt 2026-07-03 abends): privateDefault (vorher Mx9Privat; oauth), apiDefault (vorher TestWürth; apikey — 2026-07-03 kurz auf oauth, am selben Tag zurück auf apikey via `apiKeyHelper` in Pool-settings.json; model claude-sonnet-5). Alle 7 Projekte → privateDefault; apiDefault derzeit ohne Projekt. Pool-Wechsel eines Projekts = nur noch `ai-control.json` ändern (einzige Quelle; zieht beim nächsten Start). Pool-Umbenennung ändert den Pfad und damit den Keychain-Suffix — alte Einträge greifen dann nicht mehr. +- `CLAUDE.md`, `commands/`, `settings.json` liegen als physische Kopien in jedem Pool — bewusst poolspezifisch, keine Symlinks. +- Zuordnung: `~/claude-projects//ai-control.json` `{ "pool": ... }`. +- Starter: `~/Applications/Ghostty-.app`, `LSEnvironment` setzt nur noch XDG_CONFIG_HOME (stabil, nie poolabhängig). Alle Ghostty-Configs starten `command = ~/.local/bin/claude-sync` (ggf. mit `--add-dir`-Args). `claude-sync` löst seit 2026-07-04 den Pool selbst auf (liest `./ai-control.json` im CWD, exportiert `CLAUDE_CONFIG_DIR`, laut scheiternd ohne Pool), macht git pull davor und commit+push danach. `claude-start` ist gelöscht — in `claude-sync` integriert. Env/plist tragen keinen Pool mehr. +- Pool-Guard (seit 2026-07-04): `claude-sync` schreibt Sentinel `.ai-control-running` in den Projekt-Root (Inhalt pool=CLAUDE_CONFIG_DIR, pid; FD offen gehalten, nach claude-Ende gelöscht, in .gitignore). Vorab-Check (Endstand 2026-07-03 spät): Fensterzahl statt Prozesszustand, ohne Apple-Events — `ghostty-wincount ` (Swift-Helfer, CGWindowListCopyWindowInfo onScreenOnly + Layer 0; Binary `~/.local/bin/ghostty-wincount`, Quelle `~/claude-projects/robotunits/bin/ghostty-wincount.swift`; App-pid via ps+grep auf den App-Pfad). Das Skript läuft selbst in Fenster 1; ≥2 → Arbeitsfenster existiert → `open -a` (Launch Services, kein Prompt) + 4 s + Exit 0; sonst → Sentinel-pid und FD-Holder kill -9, Sentinel kommentarlos löschen, normal starten. Gescheiterte Vorstufen: tty-Heuristik (Zombie behält totes tty), Schreiber-PID-Check (claude-sync-Bash überlebt Ghosttys X-Close manchmal kopflos), osascript count/activate (Automation-Prompt „App X will App Y steuern" = NOGO). Bekannte Grenze: minimierte/Space-fremde Fenster zählen als unsichtbar. Traps in claude-sync: EXIT löscht Sentinel, HUP/TERM killt claude (claude läuft als Hintergrund-Kind mit `<&0` — sonst stdin=/dev/null! — und `wait`), INT no-op; greifen aber bei Ghostty-X/Quit oft nicht (hartes Kill), Rest-Aufräumen beim nächsten Start ist der Normalfall. Alle 7 Ghostty-Configs (`~/.config-ghostty//ghostty/config`): `wait-after-command = false`; claude-sync zeigt Schlussmeldungen per sleep (2 s aktivieren, 3 s Ende, 5 s Fehler), dann schließt das Fenster selbst. SessionStart-Hook `~/claude-projects/robotunits/bin/pool-guard` in allen 7 Projekt-settings.json: Sentinel fehlt / Pool-Mismatch / Schreiber-PID tot → rot blinkender Banner auf /dev/tty + kill des claude-Elternprozesses. RAW-Nutzung außerhalb der Projekte bleibt frei; RAW-Start IM Projekt wird beendet. Achtung: greift auch bei /clear in Sessions, die noch ohne Sentinel gestartet wurden. +- Gelöscht: `~/.claude` (komplett), Keychain-Eintrag `Claude Code-credentials`, `~/claude-pools`. Unterhaltungen (90 MB) + `history.jsonl` nach Mx9Privat umgezogen — `/resume` pro Pool. +- App-Code (`~/projects/ai-control`, Tauri): Kernlogik mit injizierbarem Home, 22 Tests (`./build.sh`-Toolchain: CARGO_HOME=~/tools/.cargo; node via fnm, nur in interaktiver zsh). Commands unassign_pool/create_project/delete_project, seit 2026-07-04 restart_project (osascript-Quit → warten → open) + Modal in ProjectList: nach Umhängen eines laufenden Projekts Restart-Angebot inkl. Hinweis, dass der claude-sync-Push beim Abschießen entfällt. +- Eingebautes Terminal (2026-07-04, Prototyp, funktioniert laut Marcus): `src-tauri/src/terminal.rs` (portable-pty, Commands open_terminal/term_start/term_write/term_resize) + `terminal.html`/`src/terminal.ts` (xterm.js, Output base64-Events, Capability `term-*`). „Terminal"-Button in ProjectList öffnet pro Klick ein eigenes Fenster; PTY startet `zsh -ic claude-sync` im Projektordner — Pool/Sentinel/git-Sync unverändert bei claude-sync, App setzt kein CLAUDE_CONFIG_DIR. Fenster-X killt Kind + schließt PTY (HUP wie Ghostty-X, Abschluss-Push entfällt). Damit Weg frei, die Ghostty-Starter abzulösen ("wir haben alles in der Hand"). Offen: Scrollback-Restore, Fensterposition merken. +- Stand 2026-07-04 abends, alles committed+gepusht (bis inkl. Todo-Feature): UI komplett auf Catppuccin-Mocha-Token + JetBrains Mono (frontend-design-Skill; Plugin `frontend-design` war kaputt — installPaths in installed_plugins.json zeigten nach Pool-Umbenennung noch auf Mx9Privat, gefixt), i18n de/en (vue-i18n, Umschalter im Header). Pools: löschbar mit Zuordnungs-Auflösung, „Neu anmelden" löscht vorher den suffixierten Keychain-Eintrag (nur bei ungenutztem Pool, Warn-Dialog), Renew entfernt (Datei-Refresh-Token tot, Keychain hat Vorrang), hasCredentials (apikey-Dateicheck) mit Start-Blockierung. Verbrauch-Tab: usage_stats parst /projects/*/*.jsonl (Dedup message.id+requestId, Preistabelle Fable 10/50 Opus 5/25 Sonnet 3/15 Haiku 1/5, Cache 1.25x/0.1x), Pool-Zeilen aggregiert + Projekte aufklappbar; apiDefault hat keinerlei Transcripts (Marcus' apiKey-Abfragen vom 04.07. liefen nachweislich nicht über claude-code auf diesem Rechner — Console zeigt die Wahrheit). Projekt-Wizard (create_project_full: Scaffold nach 08-07-Muster inkl. pool-guard-Hook, Arbeitsordner anlegen/verknüpfen) + Löschen (Guard bei laufender Session, optional Arbeitsordner aus additionalDirectories mit). Todo-Feature nur Projektebene (robotunits-Muster: OFFENE-PUNKTE.md + jq-SessionStart-Hook, zuschaltbar in Wizard+Zahnrad; Pool-Ebene bewusst nicht). App heißt ai-control (Cargo-Paket umbenannt), Hand-Icon (Tray per include_bytes + Template-Icon; Dock-Icon der Terminals via NSApplication nur in RunEvent::Ready). 38 Rust-Tests. Offen/geplant: Baustein-Bibliothek (CLAUDE.md-Marker-Mechanismus, Konzept steht: ~/.config/ai-control/blocks/, verwaltete Abschnitte zwischen ai-control:block-Markern), weitere Wizard-Optionen, Split privates Repo / offizielles GitHub-Repo (Ist-Stand als Initial-Commit ohne History; OAuth-Login-Teil wegen ToS-Grauzone fürs öffentliche Repo rausnehmen — apiKey-Teil unkritisch), Vitest für UI-Tests (Vorschlag steht), Dock-Name macOS/Wayland „später überlegen". +- Alter Zwischenstand (Commit f747379 = Terminal + Start/Beenden-Button): (a) ProjectList: ein Button pro Zeile — grün „Starten" (open_terminal) / rot „Beenden" (neues Command stop_project: SIGTERM auf exakte PIDs per `pgrep -f -- "--terminal $"`, sonst Ghostty-Quit; NIE Quit über die App-Bundle-ID — träfe die Haupt-App); is_running = Ghostty ODER --terminal-Prozess; Status-Kreis ohne Prozess unsichtbar. (b) Terminal-Config-Dialog (Zahnrad): `terminal: { theme, icon, title }` in projekt-`ai-control.json` (pool-Feld jetzt Option, Datei bleibt bei unassign wenn terminal gesetzt); 5 Themes (mocha/dracula/solarized-dark/gruvbox/one-dark, Paletten in terminal.ts, Fenster-BG gespiegelt in terminal.rs theme_background — muss synchron bleiben); Icon = PNG/ICNS-Pfad via tauri-plugin-dialog-Picker, gesetzt per NSApplication.applicationIconImage (objc2, nur in RunEvent::Ready — in setup() überschreibt macOS es wieder); Titel: Fenstertitel + Header, leer → Projektname. `tauri::generate_context!` darf nur 1× expandieren (_EMBED_INFO_PLIST, fällt nur im Release-Build auf). +- Dock-Grenze macOS: Hover-Label im Dock = Bundle-Name, zur Laufzeit nicht öffentlich änderbar; Wrapper-Bundle pro Projekt von Marcus abgelehnt („verlieren zu viel"). Windows/X11: Titel+Icon greifen fensterbasiert; Wayland-Docks bräuchten .desktop pro Projekt (xdg-toplevel-icon nur KDE). → „da müssen wir was überlegen, nicht jetzt". + +Frische Pools zeigen Promo-/Upsell-Banner („Werbung"), privateDefault nicht — Ursache ungeklärt (Stand 2026-07-04, nicht weiter vertiefen): Hochsetzen der Zähler in der Pool-`.claude.json` (`tipsHistory.fable-5-promo-2`, `passesUpsellSeenCount`) half nicht, ebenso wenig `cachedGrowthBookFeatures.tengu_birch_lantern = "off"` (einziger Flag-Unterschied der Pools). Kein Settings-Switch vorhanden. + +**Stand 2026-07-05 (Session GitHub-Release, alles committed bis 0ebdaf0):** +- **Projekt-Registry statt festem Root:** `~/.config/ai-control/projects.json` (Name → Pfad, Home-Pfade ~-kontrahiert), Alt-Layout-Scan komplett entfernt — ohne Registry keine Projekte. Marcus' 7 Projekte migriert. Neue Commands add_project (Import bestehender Ordner, UI „Projekt importieren …"), remove_project (nur Eintrag), set_project_dir (Root im ⚙︎-Dialog änderbar; schreibt autoMemoryDirectory + Edit-Permission um). Arbeitsordner nachträglich im ⚙︎-Dialog erfassbar/entfernbar (additionalDirectories + Edit-Permission, ~-kontrahiert). +- **Icons zentral:** `~/.config/ai-control/icons/.` (NICHT Pool — Marcus' Entscheidung: gemeinsames Resourcen-Verzeichnis, kein Umzug bei Pool-Wechsel, nichts im Quell-Repo). Alle 7 Projekte migriert. Nicht ladbares Icon crasht den Tray nicht mehr (eprintln + Eintrag ohne Icon; vorher Crash-Ursache: gelöschtes icon.png). +- **Terminal:** `claudeCommand` in App-settings.json (Default `claude`; bei Marcus `claude-sync`), App setzt CLAUDE_CONFIG_DIR aus dem Projekt-Pool selbst. Terminal-Fenster aktiviert sich beim Öffnen (Starter yieldActivationToApplicationWithBundleIdentifier + activate im Ready-Event — Aktivierung seit macOS 14 kooperativ, sonst Pop-under). Tray-Menü listet Projekte mit Icon + grünem Statuspunkt (56×36-RGBA im Icon-Slot, muda skaliert auf 18 pt); Klick startet bzw. fokussiert (NSRunningApplication über PID). +- **Session-Sync:** Watcher synct per Projekt das umgebende Git-Repo (add/commit/pull --rebase/push inline), git-sync-Skript-Kopplung raus. +- **GitHub-reif:** README englisch mit Screenshots (docs/), MIT-Lizenz, Anthropic-Lizenz-Hinweis (reine Pool-Verwaltung, OAuth-Login macht claude selbst). Deploy-Falle: nach rm -rf+cp nach ~/Applications `lsregister -f` nötig, sonst „App weg" in Spotlight. +- 51 Rust-Tests. Zwischenfall: Commit im falschen Repo (Shell-cwd springt auf ~/claude-projects/ai-control zurück — bei git-Befehlen immer explizit cd ~/projects/ai-control), per amend+force-with-lease bereinigt. +- **Veröffentlicht 2026-07-05:** github.com/mHinz-hub/ai-control — main = Squash-Commit ohne History (Gitea behält volle History; Remote `github` im App-Repo, HTTPS mit Fine-grained-PAT im macOS-Keychain; SSH-Key des Rechners gehört zum Arbeits-Account ex08802409_wdp, NICHT für mHinz-hub nutzbar). Tag v0.1.0 + Release mit DMG-Asset (build.sh baut app+dmg; CI=true wegen TCC). FUNDING.yml → buymeacoffee.com/marcusH. `poolSyncDir` in settings.json steuert Pool-Runtime-Verlinkung (bei Marcus ~/claude-projects/pool; ungesetzt = lokal). Release-Flow künftig: Gitea entwickeln → `git commit-tree` Squash → force-push github main → Tag → DMG via API hochladen. + +**Agenda 2026-07-05 (alt):** Sync-Test auf neuem Mac (pool-Repo mit Symlink-Runtime: clonen, `link-pool-runtime` bzw. Pool-Anlage über die App, /resume-Übernahme prüfen). + +**Why:** Getrennte Zugänge pro Pool, [[multi-account-keychain]]. + +**Agenda 2026-07-04:** Start/Stopp-Architektur grundsätzlich besprechen — Stand vom 2026-07-03 spät (wincount/kill-9/Sentinel-Reste) funktioniert im Test, ist aber Flickwerk um Ghosttys unzuverlässiges Kill-Verhalten herum; Marcus will eine saubere Lösung statt Workarounds. Außerdem: Pool-Guard + Kaltstart über Starter testen; App: UI aufräumen, Projekte managen, Defaults/Standardverhalten (z. B. Todolist); Linux gesondert; Open-Source-Frage („works for my workflow") entschieden nach Feinschliff; außerdem Ghostty und Zed besprechen. Stand App: beta-0.8 gepusht (Tray statt Dock, Autostart-Schalter, Fenster startet unsichtbar, Restart-Dialog). + +**How to apply — offen (Stand 2026-07-03):** (1) Sync-Konzept für `~/.config/ai-control` inkl. Unterhaltungen, Secrets-Frage (git). (2) UI kennt die drei neuen Commands nicht. (3) Erledigt durch Integration in `claude-sync` (2026-07-04): der „Flicker" betraf das gelöschte `claude-start`; `claude-sync` läuft in den Ghostty-Wrappern nachweislich interaktiv. diff --git a/memory/feedback-erklaerungen-frage-getrieben.md b/memory/feedback-erklaerungen-frage-getrieben.md new file mode 100644 index 0000000..f064b88 --- /dev/null +++ b/memory/feedback-erklaerungen-frage-getrieben.md @@ -0,0 +1,14 @@ +--- +name: erklaerungen-frage-getrieben +description: "Erklärthemen (auch \"für Dumme\"-Punkte) nie als Vortrag, sondern kurz und frage-getrieben" +metadata: + node_type: memory + type: feedback + originSessionId: 40d78c29-a103-4420-b913-4d49b26def7a +--- + +Marcus will sich durch Erklärthemen selbst durchfragen (Vorfall 2026-07-05, Vertex-Aufklärung: langer Vortrag kam schlecht an). + +**Why:** Auch wenn ein offener Punkt "von Grund auf erklären" sagt, heißt das nicht Monolog — er steuert per Fragen, Antworten knapp, keine Wiederholungen, ihn nicht für ahnungslos halten. + +**How to apply:** Bei Aufklärungs-/Lernthemen: kurzer Einstieg (wenige Sätze oder kompakte Tabelle), dann auf seine Fragen antworten. Nur exakt das Gefragte. diff --git a/memory/feedback-go-pflicht.md b/memory/feedback-go-pflicht.md new file mode 100644 index 0000000..e4d101f --- /dev/null +++ b/memory/feedback-go-pflicht.md @@ -0,0 +1,14 @@ +--- +name: feedback-go-pflicht +description: Weitreichende Änderungen NIE ohne ausdrückliches Go; eine Rückfrage von Marcus ist kein Go +metadata: + node_type: memory + type: feedback + originSessionId: 6c3fc02d-bc5c-4476-9384-8062394a85ff +--- + +Weitreichende Änderungen (Verhaltensänderungen im Backend, Löschlogik, alles mit Datenverlust-Potenzial) nie umsetzen, bevor Marcus ausdrücklich Go gegeben hat. Eine Rückfrage oder Diskussionsfrage von Marcus („was machen wir dann mit X?") ist KEIN Go — Frage beantworten, dann stoppen und warten. + +**Why:** 2026-07-04: Nach Marcus' Rückfrage zur Pool-Löschung habe ich direkt implementiert; Marcus hat abgebrochen („ICH HATTE NICHT GESAGT, DASS DU DAS TUN SOLLST"). Sein „ja" galt dem Plan davor, seine Folgefrage hat die Diskussion wieder geöffnet. + +**How to apply:** Bei jedem Plan mit offener Designfrage: erst alle offenen Punkte klären, dann explizites Go für den Gesamtumfang abwarten. Antwort auf eine Frage endet mit der Antwort, nicht mit Tool-Calls. diff --git a/memory/feedback-terminal-test-08-07.md b/memory/feedback-terminal-test-08-07.md new file mode 100644 index 0000000..d321438 --- /dev/null +++ b/memory/feedback-terminal-test-08-07.md @@ -0,0 +1,20 @@ +--- +name: feedback-terminal-test-08-07 +description: "Terminal-/Restart-Tests der App immer mit Projekt 08-07, nie mit ai-control" +metadata: + node_type: memory + type: feedback + originSessionId: dd1d2798-9e9a-4e95-bf63-7793e7801020 +--- + +Für Tests des eingebauten Terminals und von restart_project immer das Projekt **08-07** als Beispiel nehmen, nie **ai-control**. + +**Why:** Die Arbeitssession läuft selbst im Projekt ai-control — ein Terminal-/Restart-Test dort killt die eigene Session und der Kontext ist weg. + +**How to apply:** In Testanweisungen, Beispielaufrufen und UI-Tests (`open_terminal`, `restart_project`) als Projektname `08-07` verwenden. Siehe [[ai-control-pool-setup]]. + +**Stand 2026-07-04:** Die Session läuft selbst im eingebauten Terminal der App (xterm.js + portable-pty). Ein Neustart/Beenden der App killt die Session direkt — die 08-07-Regel schützt nur gegen `restart_project ai-control`, nicht gegen Tests auf App-Ebene. + +**Verschärfung (2026-07-04, nach erneutem Vorfall):** Alles, was den App-Prozess oder das eigene Terminal berührt, ist tabu — App-Neustart, App-Build+Relaunch, Kill von Electron-/pty-Prozessen, `pkill`/`kill` auf Prozesse, deren Zugehörigkeit nicht geprüft ist. Vor jedem Kill/Restart erst prüfen, ob der Zielprozess in der eigenen Prozesskette hängt. Solche Tests laufen nur nach Ansage von Marcus und dann in 08-07 bzw. einer separaten App-Instanz. + +**Absolutes Verbot (2026-07-04, dritter Vorfall — Kontext erneut verloren):** NIE ein Terminal für oder aus ai-control starten — kein `open_terminal ai-control`, kein Terminal-Start über die App, keine Ausnahme „nur kurz testen". Jeder Terminal-Start, der ai-control betrifft, killt diese Session. Terminal-Funktionen werden ausschließlich mit 08-07 getestet, und nur wenn Marcus es ausdrücklich anweist. diff --git a/memory/feedback-warum-ist-keine-suchlizenz.md b/memory/feedback-warum-ist-keine-suchlizenz.md new file mode 100644 index 0000000..7a179af --- /dev/null +++ b/memory/feedback-warum-ist-keine-suchlizenz.md @@ -0,0 +1,14 @@ +--- +name: feedback-warum-ist-keine-suchlizenz +description: "Auch eine dringliche 'warum'-Frage ist keine Erlaubnis zum Suchen/Ausführen — nur aus Wissen/Memory beantworten, sonst 'weiß ich nicht' sagen" +metadata: + node_type: memory + type: feedback + originSessionId: 9ca637e1-89b8-4a5f-beaf-f2eab81d2302 +--- + +Wenn Marcus "warum" fragt (auch wiederholt, auch in Großbuchstaben/dringlich), ist das KEINE explizite Suchanweisung. Die Regel „nur suchen, wenn ausdrücklich angewiesen" gilt unverändert — eine Faktenfrage wird aus vorhandenem Wissen/Memory beantwortet, nicht durch Bash/Grep/Read „belegt". + +**Why:** Vorfall 2026-07-04 — auf wiederholtes "WARUM" habe ich eigenmächtig `cat`/`ls`/env-Check ausgeführt, obwohl [[feedback-go-pflicht]] und die globale Suchregel das explizit verbieten. Marcus hat den Tool-Call abgelehnt und auf den „Vertrag" verwiesen. Dringlichkeit/Großschreibung der Frage ändert nichts an der Erlaubnis. + +**How to apply:** Bei „warum"-Fragen ohne explizites „such nach…", „schau nach…", „prüf…": nur aus bereits geladenem Kontext/Memory antworten, notfalls „weiß ich nicht ohne nachzuschauen" sagen — nicht eigenständig Bash/Read/Grep/Agent starten, egal wie dringlich die Frage klingt. diff --git a/memory/linux-umzug.md b/memory/linux-umzug.md new file mode 100644 index 0000000..3bb2d6b --- /dev/null +++ b/memory/linux-umzug.md @@ -0,0 +1,16 @@ +--- +name: linux-umzug +description: ai-control soll unter Linux laufen; Sessions laufen ab 2026-07-05 auf der Linux-Box weiter +metadata: + node_type: memory + type: project + originSessionId: 7fa72046-ab7a-4b41-a496-f0f6b9a4729d +--- + +Stand 2026-07-05: Marcus testet ai-control auf seiner Linux-Box und will die Arbeit dort fortsetzen — die Mac-Session wurde dafür beendet. Linux- und Windows-Integration sind im README bereits angekündigt. + +**Wie gestartet wird:** `~/claude-projects/ai-control/start-claude.sh` — bewusst nur ein Einzeiler: `cd` ins Skriptverzeichnis, `CLAUDE_CONFIG_DIR=~/.config/ai-control/pools/private`, `exec claude`. Die Pfade sind auf beiden Maschinen identisch (`~`-relativ), der Transport läuft über den claude-projects-Git-Sync (Gitea). + +**Gescheitert und verworfen:** ein parametrisiertes Setup-/Startskript-Paar (setup-config.sh legte nur ein leeres Skelett an statt die echte Config zu migrieren; start-claude.sh verlangte einen Projektnamen). Marcus wollte genau dieses eine Projekt und keine zusätzliche Konfiguration pro Maschine. Lehre: [[feedback-go-pflicht]] — erst das Minimalste bauen, das er wörtlich beschreibt. + +**Offen für Linux:** macOS-spezifischer Code in der App (objc2-Aktivierung, sips-Icons, Tray) muss hinter `cfg(target_os)`; Build mit webkit2gtk/AppIndicator ungetestet. Siehe [[ai-control-pool-setup]]. diff --git a/memory/multi-account-keychain.md b/memory/multi-account-keychain.md new file mode 100644 index 0000000..df69688 --- /dev/null +++ b/memory/multi-account-keychain.md @@ -0,0 +1,22 @@ +--- +name: multi-account-keychain +description: "Multi-Account auf macOS — Stand 2.1.199: pro CLAUDE_CONFIG_DIR eigener suffixierter Keychain-Eintrag; .credentials.json-Ansatz überholt" +metadata: + node_type: memory + type: project + originSessionId: e108ad75-f176-43b6-901d-c3ec19ec3961 +--- + +ÜBERHOLT (galt bis Version vor 2.1.199): claude las die `.credentials.json` direkt aus `CLAUDE_CONFIG_DIR` (verifiziert 2026-07-03 vormittags, Test `bin/oauth-configdir-test`). + +Stand 2026-07-03 abends, Version 2.1.199 (native Installation): Neu-Login war nötig. Die `.credentials.json` im Pool `Mx9Privat` ist weg; claude speichert jetzt pro Config-Dir einen Keychain-Eintrag `Claude Code-credentials-`, Suffix = erste 8 Hex-Zeichen von SHA-256 über den `CLAUDE_CONFIG_DIR`-Pfad (Mx9Privat → `a014e206`, TestWürth → erwartet `4c0ad9a9`, ggf. NFC/NFD-Frage wegen „ü"). Ohne gesetztes `CLAUDE_CONFIG_DIR` entsteht der unsuffixierte Eintrag `Claude Code-credentials` plus `~/.claude`/`~/.claude.json`. Damit ist #20553 (Überschreiben) upstream gelöst — pro Pool ein eigener Eintrag. Verifiziert 2026-07-03 abends: Nach `/login` in ai-control (Mx9Privat) lief wuerth-plato (gleicher Pool) ohne erneutes Login — Eintrag `a014e206` wird poolweit geteilt. Keychain enthielt danach nur diesen einen Claude-Code-Eintrag (kein unsuffixierter). Zweiter Pool ebenfalls verifiziert: 08-07 (TestWürth, oauth) verlangte wie erwartet erneutes Login und erzeugte `Claude Code-credentials-4c0ad9a9` — Suffix stimmt mit der Vorab-Berechnung überein, NFC/NFD-Frage wegen „ü" damit geklärt. Keychain-Stand: genau zwei Einträge, `a014e206` (Mx9Privat) und `4c0ad9a9` (TestWürth). Multi-Account über suffixierte Keychain-Einträge funktioniert damit vollständig. Dritter Beleg: robotunits nach Umzug auf TestWürth ohne Login gestartet (Starter-Pfad hasht identisch auf `4c0ad9a9`). Mit gesetztem `CLAUDE_CONFIG_DIR` entsteht dabei kein neues `~/.claude` im Home — das gelöschte bleibt weg. + +Umbenennung 2026-07-03 spätabends: Mx9Privat → privateDefault, TestWürth → apiDefault. Suffixe: privateDefault → `096c4ef9`, apiDefault → `15e10251`. Die alten Einträge `a014e206`/`4c0ad9a9` wurden gelöscht; kurz darauf lag `096c4ef9` im Keychain — eine laufende Session hat die Credentials unter dem neuen Pfad selbst neu geschrieben, ohne Login-Prompt. Weitere Session-Starts erzeugten keinen zusätzlichen Eintrag. Keychain-Endstand 2026-07-03: genau ein Eintrag `096c4ef9`; für apiDefault (`15e10251`) existiert noch keiner, dort ist beim ersten Start Login nötig. + +Kaltstart-Test über die umgestellten Starter (2026-07-04): durchgeführt, funktioniert grundsätzlich (kein Login nötig), aber laut Marcus nicht in der gewünschten Qualität — worin genau der Mangel liegt, ist noch nicht festgehalten. + +Umbenennung 2026-07-05: privateDefault → private (Runtime-Ordner, Repo-Ordner, Symlinks, alle 7 Projekt-ai-control.json). Neuer Suffix `485a513f`, von der laufenden Session ohne Login selbst geschrieben; `096c4ef9` gelöscht. Im Keychain liegen daneben vier ungeklärte Einträge (54743385, 550a1032, b425db93, dfbfcf1a), keinem Pool zugeordnet — vermutlich Test-Reste. + +**Why:** Mehrere Claude-Zugänge (Abo + Kunden-Keys) strikt getrennt auf einem Rechner. + +**How to apply:** Pro Pool ein Ordner als `CLAUDE_CONFIG_DIR` (siehe [[ai-control-pool-setup]]). OAuth: `.credentials.json` (0600) in den Pool-Ordner. API-Key: `apiKeyHelper` in der Pool-`settings.json` auf die Key-Datei. `CLAUDE_CODE_OAUTH_TOKEN`-Export ist unnötig. diff --git a/migrate-linux.sh b/migrate-linux.sh new file mode 100755 index 0000000..2bf182c --- /dev/null +++ b/migrate-linux.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# migrate-linux.sh — richtet ~/.config/ai-control auf dieser Maschine so ein, +# wie es auf dem Mac für das Projekt ai-control steht (übrige Projekte später). +# Quelle ist das synced Repo ~/claude-projects/pool (CLAUDE.md, settings.json, +# commands, Runtime) — deshalb: erst pullen, dann ausführen. +# Überschreibt vorhandene Config; lokale Test-Sessions im Pool werden verworfen. +set -euo pipefail + +config="$HOME/.config/ai-control" +poolrepo="$HOME/claude-projects/pool" +pooldir="$config/pools/private" + +mkdir -p "$pooldir" + +# Projekt-Icons liegen im synced Repo; unter .config nur ein Symlink +if [ -d "$config/icons" ] && [ ! -L "$config/icons" ]; then rm -rf "$config/icons"; fi +ln -sfn "$poolrepo/icons" "$config/icons" + +# App-Settings — wie auf dem Mac +cat > "$config/settings.json" <<'EOF' +{ + "claudeCommand": "claude-sync", + "poolSyncDir": "~/claude-projects/pool" +} +EOF + +# Registry: erst mal nur ai-control +cat > "$config/projects.json" <<'EOF' +{ + "ai-control": "~/claude-projects/ai-control" +} +EOF + +# Pool private: Identität + User-Scope aus dem synced Repo +cat > "$pooldir/pool.json" <<'EOF' +{ + "name": "private", + "credentialType": "oauth" +} +EOF +cp "$poolrepo/settings.json" "$pooldir/settings.json" +cp "$poolrepo/CLAUDE.md" "$pooldir/CLAUDE.md" +rm -rf "$pooldir/commands" +cp -R "$poolrepo/commands" "$pooldir/commands" + +# Runtime (projects/todos/history.jsonl) aufs synced Repo verlinken — +# damit funktionieren Resume und History maschinenübergreifend +"$poolrepo/bin/link-pool-runtime" private + +echo "migriert: $config (Projekt ai-control, Pool private)" diff --git a/release/ai-control_0.2.0_aarch64.dmg b/release/ai-control_0.2.0_aarch64.dmg new file mode 100644 index 0000000..b57950d Binary files /dev/null and b/release/ai-control_0.2.0_aarch64.dmg differ diff --git a/setup-config.sh b/setup-config.sh new file mode 100755 index 0000000..1b2536d --- /dev/null +++ b/setup-config.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Bereitet ~/.config/ai-control auf einer frischen Maschine vor — dasselbe +# Layout, das die App anlegt: +# settings.json App-Settings (claudeCommand; poolSyncDir ungesetzt = lokal) +# projects.json Projekt-Registry (Name -> Pfad, ~-relativ) +# icons/ gemeinsames Icon-Verzeichnis +# pools// oauth-Pool: pool.json, settings.json, CLAUDE.md +# Vorhandene Dateien bleiben unangetastet. +# +# Aufruf: setup-config.sh [pool-id] (Default: private) +set -euo pipefail + +pool="${1:-private}" +config="$HOME/.config/ai-control" +pooldir="$config/pools/$pool" + +mkdir -p "$config/icons" "$pooldir" + +[ -f "$config/settings.json" ] || cat > "$config/settings.json" <<'EOF' +{ + "claudeCommand": "claude" +} +EOF + +[ -f "$config/projects.json" ] || echo '{}' > "$config/projects.json" + +[ -f "$pooldir/pool.json" ] || cat > "$pooldir/pool.json" < "$pooldir/settings.json" <<'EOF' +{ + "promptSuggestionEnabled": false, + "awaySummaryEnabled": false +} +EOF + +[ -f "$pooldir/CLAUDE.md" ] || : > "$pooldir/CLAUDE.md" + +echo "fertig: $config (Pool: $pool)" diff --git a/start-claude.sh b/start-claude.sh new file mode 100755 index 0000000..03f360b --- /dev/null +++ b/start-claude.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +cd "$(dirname "$0")" +CLAUDE_CONFIG_DIR=~/.config/ai-control/pools/private exec claude