# Offene Punkte — bei jedem Start prüfen und abhaken

jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:"SessionStart", additionalContext: .}}' /Users/marcus.hinz/claude-projects/ai-control/OFFENE-PUNKTE.md


# Offene Punkte — bei jedem Start prüfen und abhaken


hat jedes Einzelprojekt im Pool einen Verweis auf die pooldirectory?


CronCreate CronDelete CronList DesignSync EnterPlanMode EnterWorktree ExitPlanMode ExitWorktree Monitor NotebookEdit PushNotification RemoteTrigger SendMessage TaskCreate TaskGet TaskList TaskOutput TaskStop TaskUpdate WebFetch WebSearch


claude claude-code-guide Explore general-purpose Plan statusline-setup

- claude: Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed. (Tools: *)

- claude-code-guide: Use this agent when the user asks questions ("Can Claude...", "Does Claude...", "How do I...") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage. (Tools: Bash, Read, WebFetch, WebSearch)

- Explore: Read-only search agent for broad fan-out searches — when answering means sweeping many files, directories, or naming conventions and you only need the conclusion, not the file dumps. It reads excerpts rather than whole files, so it locates code; it doesn't review or audit it. Specify search breadth: "medium" for moderate exploration, "very thorough" for multiple locations and naming conventions. (Tools: All tools except Agent, Artifact, ExitPlanMode, Edit, Write, NotebookEdit)

- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)

- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, Artifact, ExitPlanMode, Edit, Write, NotebookEdit)

- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)


- git-commit: 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-sync: Commit + Push aller persistent konfigurierten Projekt-Repos
- deep-research: Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. - When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., "what car to buy" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.
- dataviz: Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: "chart", "graph", "plot", "data viz", "visualization", "dashboard", "analytics", "visualize data", "categorical colors", "sequential / diverging palette", "stat tile", "sparkline", "heatmap", "legend", "axis", "tooltip", "chart colors", "color by series".
- artifact-design: Design guidance and fundamentals for Artifacts.
- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors ("from now on when X", "each time X", "whenever X", "before/after X") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions ("allow X", "add permission", "move permission to"), env vars ("set X=Y"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: "allow npm commands", "add bq permission to global settings", "move permission to user settings", "set DEBUG=true", "when claude stops show X". For simple settings like theme/model, suggest the /config command.
- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: "rebind ctrl+s", "add a chord shortcut", "change the submit key", "customize keybindings".
- verify: Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.
- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.
- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.
- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.
- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.
- schedule: Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring cloud agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run ("run this once at 3pm", "remind me to check X tomorrow").
- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.
TRIGGER — read BEFORE opening the target file; don't skip because it "looks like a one-liner" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).
SKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).
- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).
- init: Initialize a new CLAUDE.md file with codebase documentation
- review: Review a GitHub pull request; for your working diff use /code-review
- security-review: Complete a security review of the pending changes on the current branch

git-commit git-sync deep-research dataviz artifact-design update-config keybindings-help verify code-review simplify fewer-permission-prompts loop schedule claude-api run init review security-review


Das weiß ich nicht ohne nachzusehen — und suchen tue ich nur auf Ansage.


Suche mal


Bash

ls -la ~/.config/ai-control/ && echo "---POOLS---" && ls -la ~/.config/ai-control/pools/ && echo "---PROJECTS.JSON---" && cat ~/.config/ai-control/projects.json 2>/dev/null

Pool-Layout und Registry ansehen


total 40
drwxr-xr-x  7 marcus.hinz  staff   224 Jul  9 09:48 .
drwxr-xr-x  8 marcus.hinz  staff   256 Jul  3 19:11 ..
-rw-r--r--@ 1 marcus.hinz  staff  8196 Jul  9 10:20 .DS_Store
lrwxr-xr-x  1 marcus.hinz  staff    45 Jul  6 16:00 icons -> /Users/marcus.hinz/claude-projects/pool/icons
drwxr-xr-x  6 marcus.hinz  staff   192 Jul  9 10:31 pools
-rw-r--r--  1 marcus.hinz  staff   319 Jul  5 15:09 projects.json
-rw-r--r--@ 1 marcus.hinz  staff    75 Jul  9 09:48 settings.json
---POOLS---
total 24
drwxr-xr-x   6 marcus.hinz  staff   192 Jul  9 10:31 .
drwxr-xr-x   7 marcus.hinz  staff   224 Jul  9 09:48 ..
-rw-r--r--@  1 marcus.hinz  staff  8196 Jul  9 10:20 .DS_Store
drwxr-xr-x  14 marcus.hinz  staff   448 Jul  9 10:31 24d5f60d-19d9-477e-957c-a68728e79424
drwxr-xr-x  17 marcus.hinz  staff   544 Jul  4 20:02 apiDefault
drwxr-xr-x  24 marcus.hinz  staff   768 Jul  9 11:13 private
---PROJECTS.JSON---
{
  "08-07": "~/claude-projects/08-07",
  "ai-control": "~/claude-projects/ai-control",
  "limbach": "~/claude-projects/limbach",
  "misc": "~/claude-projects/misc",
  "robotunits": "~/claude-projects/robotunits",
  "wittgenstein": "~/claude-projects/wittgenstein",
  "wuerth-plato": "~/claude-projects/wuerth-plato"
}


cat ~/.config/ai-control/settings.json && echo "---" && for p in 08-07 ai-control limbach misc robotunits wittgenstein wuerth-plato; do echo "=== $p ==="; ls -la ~/claude-projects/$p/.claude/ 2>/dev/null; echo "-- settings --"; cat ~/claude-projects/$p/.claude/settings.json 2>/dev/null; done

Projekt-Settings und Pool-Verweise prüfen


{
  "claudeCommand": "claude",
  "poolSyncDir": "~/claude-projects/pool"
}
---
=== 08-07 ===
total 16
drwxr-xr-x   4 marcus.hinz  staff   128 Jul  7 23:50 .
drwxr-xr-x  13 marcus.hinz  staff   416 Jul  9 09:21 ..
-rw-r--r--   1 marcus.hinz  staff   578 Jul  4 22:04 settings.json
-rw-r--r--   1 marcus.hinz  staff  2895 Jul  7 21:20 settings.local.json
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/08-07/memory",
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/08-07/OFFENE-PUNKTE.md",
            "type": "command"
          }
        ]
      }
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/08-07"
    ],
    "allow": [
      "Edit(~/projects/08-07/**)",
      "Edit(~/claude-projects/08-07/**)"
    ]
  }
}
=== ai-control ===
total 16
drwxr-xr-x   5 marcus.hinz  staff  160 Jul  7 09:56 .
drwxr-xr-x  22 marcus.hinz  staff  704 Jul  9 10:26 ..
-rw-r--r--@  1 marcus.hinz  staff  603 Jul  5 10:19 settings.json
-rw-r--r--   1 marcus.hinz  staff  286 Jul  5 19:29 settings.local.json
drwxr-xr-x   3 marcus.hinz  staff   96 Jul  7 09:56 skills
-- settings --
{
  "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/**)"
    ]
  }
}
=== limbach ===
total 16
drwxr-xr-x  4 marcus.hinz  staff  128 Jul  4 15:48 .
drwxr-xr-x  9 marcus.hinz  staff  288 Jul  9 09:08 ..
-rw-r--r--@ 1 marcus.hinz  staff  588 Jul  4 23:28 settings.json
-rw-r--r--@ 1 marcus.hinz  staff  265 Jun 21 21:45 settings.local.json
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/limbach/memory",
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/limbach/OFFENE-PUNKTE.md",
            "type": "command"
          }
        ]
      }
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/limbach"
    ],
    "allow": [
      "Edit(~/projects/limbach/**)",
      "Edit(~/claude-projects/limbach/**)"
    ]
  }
}
=== misc ===
total 16
drwxr-xr-x   4 marcus.hinz  staff  128 Jul  4 15:48 .
drwxr-xr-x  14 marcus.hinz  staff  448 Jul  9 09:21 ..
-rw-r--r--   1 marcus.hinz  staff  532 Jul  5 10:50 settings.json
-rw-r--r--   1 marcus.hinz  staff  201 Jun 24 22:57 settings.local.json
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/misc/memory",
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/misc/OFFENE-PUNKTE.md",
            "type": "command"
          }
        ]
      }
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/misc"
    ],
    "allow": [
      "Edit(~/projects/misc/**)"
    ]
  }
}
=== robotunits ===
total 16
drwxr-xr-x   4 marcus.hinz  staff  128 Jul  4 15:48 .
drwxr-xr-x  16 marcus.hinz  staff  512 Jul  9 10:30 ..
-rw-r--r--   1 marcus.hinz  staff  853 Jul  5 11:13 settings.json
-rw-r--r--   1 marcus.hinz  staff   43 Jun 20 13:05 settings.local.json
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/robotunits/memory",
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/robotunits/OFFENE-PUNKTE.md",
            "type": "command"
          }
        ]
      }
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/geko-core",
      "~/projects/adesso-cpq",
      "~/projects/geko-infra",
      "~/projects/geko-fire"
    ],
    "allow": [
      "Edit(~/projects/geko-core/**)",
      "Edit(~/projects/adesso-cpq/**)",
      "Edit(~/projects/geko-infra/**)",
      "Edit(~/projects/fire-visual/**)",
      "Edit(~/projects/geko-fire/**)",
      "Edit(~/claude-projects/robotunits/**)"
    ]
  }
}
=== wittgenstein ===
total 16
drwxr-xr-x   5 marcus.hinz  staff  160 Jul  4 15:48 .
drwxr-xr-x  11 marcus.hinz  staff  352 Jul  9 09:21 ..
-rw-r--r--   1 marcus.hinz  staff  613 Jul  5 16:57 settings.json
-rw-r--r--   1 marcus.hinz  staff   43 Jun 20 13:05 settings.local.json
drwxr-xr-x   3 marcus.hinz  staff   96 Jun 14 11:52 skills
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/wittgenstein/memory",
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/wittgenstein/OFFENE-PUNKTE.md",
            "type": "command"
          }
        ]
      }
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/wittgenstein"
    ],
    "allow": [
      "Edit(~/projects/wittgenstein/**)",
      "Edit(~/claude-projects/wittgenstein/**)"
    ]
  }
}
=== wuerth-plato ===
total 16
drwxr-xr-x  4 marcus.hinz  staff  128 Jul  4 15:48 .
drwxr-xr-x  9 marcus.hinz  staff  288 Jul  9 09:21 ..
-rw-r--r--  1 marcus.hinz  staff  472 Jul  5 11:14 settings.json
-rw-r--r--  1 marcus.hinz  staff  924 Jun 20 13:05 settings.local.json
-- settings --
{
  "autoMemoryDirectory": "~/claude-projects/wuerth-plato/memory",
  "hooks": {
    "SessionStart": []
  },
  "permissions": {
    "additionalDirectories": [
      "~/projects/fahrzeugeinrichtung-plato-backend",
      "~/projects/fahrzeugeinrichtung-plato-gitops"
    ],
    "allow": [
      "Edit(~/projects/fahrzeugeinrichtung-plato-backend/**)",
      "Edit(~/projects/fahrzeugeinrichtung-plato-gitops/**)",
      "Edit(~/claude-projects/wuerth-plato/**)"
    ]
  }
}


Nein. Der Verweis auf die Pool-Directory (`poolSyncDir: ~/claude-projects/pool`) steht nur einmal zentral in `~/.config/ai-control/settings.json`.

Keine der sieben Projekt-`settings.json` (08-07, ai-control, limbach, misc, robotunits, wittgenstein, wuerth-plato) enthält einen Verweis auf die Pool-Directory. Sie verweisen jeweils nur auf:
- ihr eigenes `memory`-Verzeichnis (`autoMemoryDirectory`)
- ihre eigene `OFFENE-PUNKTE.md` (SessionStart-Hook)
- ihre `~/projects/...`-Arbeitsverzeichnisse (`additionalDirectories` / `Edit`-Permissions)


das /pool projekt wird von aIC bedient?


cd ~/claude-projects/ai-control && grep -rn "poolSyncDir\|claude-projects/pool" --include=*.ts --include=*.js --include=*.tsx --include=*.rs -l 2>/dev/null | head; echo "---REFS---"; grep -rn "poolSyncDir" --include=*.ts --include=*.js --include=*.tsx --include=*.rs 2>/dev/null | head -30

poolSyncDir-Nutzung im Code suchen


(eval):1: no matches found: --include=*.ts
---REFS---
(eval):1: no matches found: --include=*.ts


cd ~/projects/ai-control 2>/dev/null && pwd && grep -rn "poolSyncDir" src 2>/dev/null | head -30; echo "---also in claude-projects copy---"; cd ~/claude-projects/ai-control && grep -rln "poolSyncDir" . 2>/dev/null | grep -v node_modules | head

poolSyncDir im Quellcode suchen


/Users/marcus.hinz/projects/ai-control
---also in claude-projects copy---
migrate-linux.sh
setup-config.sh
memory/ai-control-pool-setup.md


cd ~/projects/ai-control && echo "=== src tree ==="; ls src 2>/dev/null; echo "=== pool refs in src ==="; grep -rin "pool" src 2>/dev/null | grep -vi "node_modules" | head -40

Pool-Referenzen im aktuellen Quellcode


=== src tree ===
App.vue
assets
components
i18n.ts
main.ts
popup.ts
style.css
terminal.ts
=== pool refs in src ===
src/App.vue:7:import PoolList from "./components/PoolList.vue";
src/App.vue:11:const tab = ref<"projects" | "pools" | "usage">("projects");
src/App.vue:59:      <button :class="{ active: tab === 'pools' }" @click="tab = 'pools'">
src/App.vue:60:        {{ $t("app.pools") }}
src/App.vue:90:    <PoolList v-else-if="tab === 'pools'" />
src/i18n.ts:6:    pools: "Pools",
src/i18n.ts:13:    pool: "Pool",
src/i18n.ts:27:    pool: "Pool",
src/i18n.ts:28:    noPool: "– kein Pool –",
src/i18n.ts:33:    poolChangeSaved:
src/i18n.ts:34:      "Der Pool-Wechsel ist gespeichert. Die laufende Session arbeitet aber weiter im alten Pool — der neue gilt erst ab dem nächsten Start.",
src/i18n.ts:50:    noPoolAssigned: "kein Pool",
src/i18n.ts:51:    needsPool: "Kein Pool zugeordnet",
src/i18n.ts:52:    needsKey: "Pool hat keinen API-Key",
src/i18n.ts:68:      "Angelegt werden Projektordner mit memory/, Sentinel-.gitignore, settings.json (Memory-Verzeichnis + Berechtigungen) und die Pool-/Terminal-Zuordnung.",
src/i18n.ts:73:      "Der Projektordner (inkl. memory/, Settings und Pool-Zuordnung) wird endgültig gelöscht.",
src/i18n.ts:82:  pools: {
src/i18n.ts:83:    pool: "Pool",
src/i18n.ts:95:    empty: "Noch keine Pools angelegt.",
src/i18n.ts:96:    newOauthPool: "Neuen oAuth-Pool anlegen",
src/i18n.ts:97:    newApikeyPool: "Neuen API-Key-Pool anlegen",
src/i18n.ts:102:      "Der Pool wird angelegt; die Anmeldung macht claude beim ersten Start selbst per /login (Browser gegen dein Abo).",
src/i18n.ts:104:    createPool: "Anlegen",
src/i18n.ts:107:    deletePool: "Pool löschen",
src/i18n.ts:109:      "Pool {name} und seine Credential-Datei werden gelöscht. Das lässt sich nicht rückgängig machen.",
src/i18n.ts:110:    deleteUnassigns: "Diese Projekte verlieren ihre Pool-Zuordnung:",
src/i18n.ts:119:      "Zurücksetzen ist nur bei ungenutztem Pool möglich. Diese Projekte laufen noch:",
src/i18n.ts:122:      "Keychain/Keyring ist nicht verfügbar. Der Key kann stattdessen ungesichert als Datei im Pool-Ordner liegen (0600).",
src/i18n.ts:130:    pools: "Pools",
src/i18n.ts:137:    pool: "Pool",
src/i18n.ts:151:    pool: "Pool",
src/i18n.ts:152:    noPool: "– no pool –",
src/i18n.ts:157:    poolChangeSaved:
src/i18n.ts:158:      "The pool change is saved. The running session keeps working in the old pool — the new one applies from the next start.",
src/i18n.ts:174:    noPoolAssigned: "no pool",
src/i18n.ts:175:    needsPool: "No pool assigned",
src/i18n.ts:176:    needsKey: "Pool has no API key",
src/i18n.ts:192:      "Creates the project folder with memory/, sentinel .gitignore, settings.json (memory dir + permissions), and the pool/terminal assignment.",
src/i18n.ts:197:      "The project folder (incl. memory/, settings and pool assignment) will be deleted permanently.",
src/i18n.ts:206:  pools: {


cd ~/projects/ai-control && echo "=== top level ==="; ls; echo "=== projects.json readers ==="; grep -rln "projects.json\|projectsFile\|poolSyncDir" . 2>/dev/null | grep -v node_modules | grep -v "/target/" | head

Wer liest projects.json / poolSyncDir


=== top level ===
LICENSE
README.md
build.sh
dev.sh
dist
docs
index.html
node_modules
package-lock.json
package.json
popup.html
public
src
src-tauri
terminal.html
tsconfig.app.json
tsconfig.json
tsconfig.node.json
vite.config.ts
=== projects.json readers ===
README.md
src-tauri/src/lib.rs


cd ~/projects/ai-control && grep -n "poolSyncDir\|projects.json\|claude-projects/pool\|\"pool\"\|/pool" src-tauri/src/lib.rs 2>/dev/null | head -40

pool-Referenzen im Rust-Backend


19:const PROJECTS_FILE: &str = "projects.json";
73:/// Registry Name → Projektordner; ohne projects.json gibt es keine Projekte.
1219:/// Sync-Ziel für Pool-Laufzeitdaten (settings.json: poolSyncDir).
1225:    .and_then(|v| v["poolSyncDir"].as_str().map(|d| expand_home(paths, d)))
1228:/// Zielort der synced Runtime-Daten eines Pools unterhalb von poolSyncDir.
1232:    .ok_or_else(|| "poolSyncDir ist nicht konfiguriert".to_string())
2351:    let dir = PathBuf::from("/pools/abc");
2353:    assert!(cmd.ends_with("|| cat '/pools/abc/apikey'"));
2804:      "{ \"poolSyncDir\": \"~/claude-projects/pool\" }\n",
2817:    // Zieldaten liegen unter dem konfigurierten poolSyncDir/<id>/
2818:    assert!(p.home.join("claude-projects/pool").join(&id).is_dir());
2821:  /// Ohne poolSyncDir bleibt alles lokal: keine Symlinks, Verlinken scheitert laut.
2830:    assert!(err.contains("poolSyncDir"));
2924:    assert_eq!(cfg, serde_json::json!({ "pool": kunde }));


1215	/// Prompt-Historie. (name, ist_ordner)
1216	const SYNCED_RUNTIME: [(&str, bool); 3] =
1217	  [("projects", true), ("todos", true), ("history.jsonl", false)];
1218
1219	/// Sync-Ziel für Pool-Laufzeitdaten (settings.json: poolSyncDir).
1220	/// Ungesetzt = Feature aus, alle Daten bleiben lokal im Pool-Ordner.
1221	fn pool_sync_dir(paths: &Paths) -> Option<PathBuf> {
1222	  fs::read_to_string(paths.config_dir().join(APP_SETTINGS_FILE))
1223	    .ok()
1224	    .and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
1225	    .and_then(|v| v["poolSyncDir"].as_str().map(|d| expand_home(paths, d)))
1226	}
1227
1228	/// Zielort der synced Runtime-Daten eines Pools unterhalb von poolSyncDir.
1229	fn pool_data_dir(paths: &Paths, pool: &str) -> Result<PathBuf, String> {
1230	  pool_sync_dir(paths)
1231	    .map(|d| d.join(pool))
1232	    .ok_or_else(|| "poolSyncDir ist nicht konfiguriert".to_string())
1233	}
1234
1235	/// Ersetzt im Pool-Ordner projects/todos/history.jsonl durch Symlinks auf den
1236	/// konfigurierten Sync-Ordner. Die Symlinks sind maschinenlokal, die Daten
1237	/// reisen über den Sync des Zielordners (z. B. git).
1238	/// Vorhandene echte Inhalte werden verworfen (kein History-Erhalt — bewusst).
1239	fn link_pool_runtime_in(paths: &Paths, pool: &str) -> Result<(), String> {
1240	  check_name(pool)?;
1241	  let src = paths.pool_dir(pool);
1242	  let data = pool_data_dir(paths, pool)?;
1243	  for (name, is_dir) in SYNCED_RUNTIME {
1244	    let target = data.join(name);
1245	    if is_dir {
1246	      fs::create_dir_all(&target).map_err(|e| e.to_string())?;
1247	    } else {
1248	      fs::create_dir_all(&data).map_err(|e| e.to_string())?;
1249	      if !target.exists() {
1250	        fs::write(&target, "").map_err(|e| e.to_string())?;
1251	      }
1252	    }
1253	    let link = src.join(name);
1254	    if link.is_dir() && !link.is_symlink() {
1255	      fs::remove_dir_all(&link).map_err(|e| e.to_string())?;
1256	    } else if link.is_symlink() || link.exists() {
1257	      fs::remove_file(&link).map_err(|e| e.to_string())?;
1258	    }
1259	    std::os::unix::fs::symlink(&target, &link).map_err(|e| e.to_string())?;
1260	  }
1261	  Ok(())
1262	}
1263
1264	/// Legt einen apikey-Pool an: Key in den Keychain/Keyring (Datei 0600 nur mit
1265	/// allow_file), settings.json mit apiKeyHelper-Kette, CLAUDE.md, pool.json.
1266	/// Ohne Store und ohne allow_file bricht die Anlage ab, bevor etwas entsteht.
1267	/// Liefert die Pool-ID.
1268	fn create_apikey_pool_in(
1269	  paths: &Paths,
1270	  store: &dyn ApikeyStore,
1271	  name: &str,
1272	  key: &str,
1273	  allow_file: bool,
1274	) -> Result<String, String> {
1275	  let dir = check_new_pool(paths, name)?;
1276	  let key = key.trim();
1277	  if key.is_empty() {
1278	    return Err("leerer API-Key".into());
1279	  }
1280	  let id = dir.file_name().unwrap().to_string_lossy().into_owned();
1281	  if store.set(&id, key).is_err() {
1282	    if !allow_file {
1283	      return Err(KEYCHAIN_UNAVAILABLE.into());
1284	    }
1285	    write_secret_file(&dir.join(APIKEY_FILE), &format!("{key}\n"))?;
1286	  }
1287	  init_pool_config(
1288	    &dir,
1289	    serde_json::json!({ "apiKeyHelper": apikey_helper_command(&dir, &id) }),
1290	  )?;
1291	  write_pool_json(&dir, name, "apikey")?;
1292	  if pool_sync_dir(paths).is_some() {
1293	    link_pool_runtime_in(paths, &id)?;
1294	  }

/// Prompt-Historie. (name, ist_ordner)
const SYNCED_RUNTIME: [(&str, bool); 3] =
  [("projects", true), ("todos", true), ("history.jsonl", false)];

/// Sync-Ziel für Pool-Laufzeitdaten (settings.json: poolSyncDir).
/// Ungesetzt = Feature aus, alle Daten bleiben lokal im Pool-Ordner.
fn pool_sync_dir(paths: &Paths) -> Option<PathBuf> {
  fs::read_to_string(paths.config_dir().join(APP_SETTINGS_FILE))
    .ok()
    .and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
    .and_then(|v| v["poolSyncDir"].as_str().map(|d| expand_home(paths, d)))
}

/// Zielort der synced Runtime-Daten eines Pools unterhalb von poolSyncDir.
fn pool_data_dir(paths: &Paths, pool: &str) -> Result<PathBuf, String> {
  pool_sync_dir(paths)
    .map(|d| d.join(pool))
    .ok_or_else(|| "poolSyncDir ist nicht konfiguriert".to_string())
}

/// Ersetzt im Pool-Ordner projects/todos/history.jsonl durch Symlinks auf den
/// konfigurierten Sync-Ordner. Die Symlinks sind maschinenlokal, die Daten
/// reisen über den Sync des Zielordners (z. B. git).
/// Vorhandene echte Inhalte werden verworfen (kein History-Erhalt — bewusst).
fn link_pool_runtime_in(paths: &Paths, pool: &str) -> Result<(), String> {
  check_name(pool)?;
  let src = paths.pool_dir(pool);
  let data = pool_data_dir(paths, pool)?;
  for (name, is_dir) in SYNCED_RUNTIME {
    let target = data.join(name);
    if is_dir {
      fs::create_dir_all(&target).map_err(|e| e.to_string())?;
    } else {
      fs::create_dir_all(&data).map_err(|e| e.to_string())?;
      if !target.exists() {
        fs::write(&target, "").map_err(|e| e.to_string())?;
      }
    }
    let link = src.join(name);
    if link.is_dir() && !link.is_symlink() {
      fs::remove_dir_all(&link).map_err(|e| e.to_string())?;
    } else if link.is_symlink() || link.exists() {
      fs::remove_file(&link).map_err(|e| e.to_string())?;
    }
    std::os::unix::fs::symlink(&target, &link).map_err(|e| e.to_string())?;
  }
  Ok(())
}

/// Legt einen apikey-Pool an: Key in den Keychain/Keyring (Datei 0600 nur mit
/// allow_file), settings.json mit apiKeyHelper-Kette, CLAUDE.md, pool.json.
/// Ohne Store und ohne allow_file bricht die Anlage ab, bevor etwas entsteht.
/// Liefert die Pool-ID.
fn create_apikey_pool_in(
  paths: &Paths,
  store: &dyn ApikeyStore,
  name: &str,
  key: &str,
  allow_file: bool,
) -> Result<String, String> {
  let dir = check_new_pool(paths, name)?;
  let key = key.trim();
  if key.is_empty() {
    return Err("leerer API-Key".into());
  }
  let id = dir.file_name().unwrap().to_string_lossy().into_owned();
  if store.set(&id, key).is_err() {
    if !allow_file {
      return Err(KEYCHAIN_UNAVAILABLE.into());
    }
    write_secret_file(&dir.join(APIKEY_FILE), &format!("{key}\n"))?;
  }
  init_pool_config(
    &dir,
    serde_json::json!({ "apiKeyHelper": apikey_helper_command(&dir, &id) }),
  )?;
  write_pool_json(&dir, name, "apikey")?;
  if pool_sync_dir(paths).is_some() {
    link_pool_runtime_in(paths, &id)?;
  }


Teils. Es kommt darauf an, was mit „bedient" gemeint ist.

**Als Projekt: nein.** `~/claude-projects/pool` steht nicht in `projects.json`, ist also kein von aIC verwaltetes Projekt (keine Session, kein Pool-/Terminal-Eintrag).

**Als `poolSyncDir`: ja.** Der Rust-Code (`lib.rs`) nutzt `~/claude-projects/pool` als Sync-Ziel für Pool-Laufzeitdaten:
- Pro Credential-Pool legt aIC dort ein Unterverzeichnis `pool/<pool-id>/` an.
- Dorthin werden die synced Runtime-Daten `projects/`, `todos/`, `history.jsonl` geschrieben (`SYNCED_RUNTIME`, `link_pool_runtime_in`).
- Im eigentlichen Pool-Ordner (`~/.config/ai-control/pools/<id>/`) ersetzt aIC diese drei durch Symlinks auf das Sync-Ziel. Ausgelöst bei Pool-Anlage/Verlinkung, aber nur wenn `poolSyncDir` gesetzt ist (ist es).

**Nicht von aIC bedient:** die übrigen Inhalte von `pool/` — `icons/` (Symlink-Quelle) und die globale `CLAUDE.md` — sind manuell/extern gepflegt, der App-Code fasst sie nicht an.


das passt aber nicht
aIC
- ist besitzer des /pool verzeichnisses -> legt dort die PoolCofigs ab sollte auch die entsprechenden Symlinks asnlegen


echo "=== ~/claude-projects/pool ==="; ls -la ~/claude-projects/pool/ 2>/dev/null; echo; echo "=== ~/.config/ai-control/pools ==="; ls -la ~/.config/ai-control/pools/; echo; echo "=== ein Pool-Ordner: private ==="; ls -la ~/.config/ai-control/pools/private/

Inhalt von pool-Sync-Dir und Pool-Config-Dir vergleichen


=== ~/claude-projects/pool ===
total 56
drwxr-xr-x  13 marcus.hinz  staff    416 Jul  9 09:47 .
drwxr-xr-x  10 marcus.hinz  staff    320 Jul  9 09:25 ..
-rw-r--r--@  1 marcus.hinz  staff  10244 Jul  9 09:49 .DS_Store
drwxr-xr-x  14 marcus.hinz  staff    448 Jul  9 09:48 .git
-rw-r--r--   1 marcus.hinz  staff     31 Jul  9 09:21 .gitignore
drwxr-xr-x   5 marcus.hinz  staff    160 Jul  5 16:54 24d5f60d-19d9-477e-957c-a68728e79424
-rw-r--r--   1 marcus.hinz  staff   4826 Jul  4 19:08 CLAUDE.md
drwxr-xr-x   5 marcus.hinz  staff    160 Jul  4 20:02 apiDefault
drwxr-xr-x   5 marcus.hinz  staff    160 Jul  9 09:36 bin
drwxr-xr-x   3 marcus.hinz  staff     96 Jul  4 18:59 commands
drwxr-xr-x   9 marcus.hinz  staff    288 Jul  5 15:09 icons
drwxr-xr-x   5 marcus.hinz  staff    160 Jul  9 14:31 private
-rw-r--r--@  1 marcus.hinz  staff    629 Jul  4 18:59 settings.json

=== ~/.config/ai-control/pools ===
total 24
drwxr-xr-x   6 marcus.hinz  staff   192 Jul  9 10:31 .
drwxr-xr-x   7 marcus.hinz  staff   224 Jul  9 09:48 ..
-rw-r--r--@  1 marcus.hinz  staff  8196 Jul  9 10:20 .DS_Store
drwxr-xr-x  14 marcus.hinz  staff   448 Jul  9 10:31 24d5f60d-19d9-477e-957c-a68728e79424
drwxr-xr-x  17 marcus.hinz  staff   544 Jul  4 20:02 apiDefault
drwxr-xr-x  24 marcus.hinz  staff   768 Jul  9 14:06 private

=== ein Pool-Ordner: private ===
total 176
drwxr-xr-x   24 marcus.hinz  staff    768 Jul  9 14:06 .
drwxr-xr-x    6 marcus.hinz  staff    192 Jul  9 10:31 ..
-rw-r--r--@   1 marcus.hinz  staff   6148 Jul  4 15:59 .DS_Store
-rw-------    1 marcus.hinz  staff  47046 Jul  9 14:06 .claude.json
-rw-------    1 marcus.hinz  staff    391 Jul  4 13:11 .credentials.json
-rw-r--r--    1 marcus.hinz  staff     24 Jul  9 12:36 .last-cleanup
-rw-r--r--    1 marcus.hinz  staff    161 Jul  9 07:55 .last-update-result.json
-rw-r--r--    1 marcus.hinz  staff   4826 Jul  4 19:08 CLAUDE.md
drwxr-xr-x    7 marcus.hinz  staff    224 Jul  9 14:06 backups
drwxr-xr-x    3 marcus.hinz  staff     96 Jul  3 18:26 cache
drwxr-xr-x    3 marcus.hinz  staff     96 Jul  4 18:59 commands
drwxr-xr-x   33 marcus.hinz  staff   1056 Jul  9 09:36 file-history
lrwxr-xr-x    1 marcus.hinz  staff     61 Jul  5 11:29 history.jsonl -> /Users/marcus.hinz/claude-projects/pool/private/history.jsonl
drwxr-xr-x   10 marcus.hinz  staff    320 Jul  9 11:25 paste-cache
drwxr-xr-x    9 marcus.hinz  staff    288 Jul  9 12:18 plugins
-rw-r--r--    1 marcus.hinz  staff     53 Jul  5 11:29 pool.json
lrwxr-xr-x    1 marcus.hinz  staff     56 Jul  5 11:29 projects -> /Users/marcus.hinz/claude-projects/pool/private/projects
drwxr-xr-x  222 marcus.hinz  staff   7104 Jul  9 10:40 session-env
drwx------    4 marcus.hinz  staff    128 Jul  9 14:06 sessions
-rw-r--r--    1 marcus.hinz  staff    619 Jul  8 08:04 settings.json
drwxr-xr-x   29 marcus.hinz  staff    928 Jul  9 12:24 shell-snapshots
-rw-------    1 marcus.hinz  staff   1851 Jul  7 13:42 stats-cache.json
drwxr-xr-x    3 marcus.hinz  staff     96 Jul  7 18:23 tasks
lrwxr-xr-x    1 marcus.hinz  staff     53 Jul  5 11:29 todos -> /Users/marcus.hinz/claude-projects/pool/private/todos


echo "=== pool/private (sync-ziel) ==="; ls -la ~/claude-projects/pool/private/; echo; echo "=== .gitignore ==="; cat ~/claude-projects/pool/.gitignore; echo; echo "=== git tracked (top) ==="; cd ~/claude-projects/pool && git ls-files | sed 's#/.*##' | sort -u

Sync-Ziel-Inhalt und Git-Tracking prüfen


=== pool/private (sync-ziel) ===
total 384
drwxr-xr-x   5 marcus.hinz  staff     160 Jul  9 14:31 .
drwxr-xr-x  13 marcus.hinz  staff     416 Jul  9 09:47 ..
-rw-r--r--   1 marcus.hinz  staff  195859 Jul  9 14:31 history.jsonl
drwxr-xr-x   9 marcus.hinz  staff     288 Jul  7 14:13 projects
drwxr-xr-x   2 marcus.hinz  staff      64 Jul  4 20:08 todos

=== .gitignore ===
!.claude/
!CLAUDE.md
.DS_Store

=== git tracked (top) ===
.gitignore
24d5f60d-19d9-477e-957c-a68728e79424
CLAUDE.md
apiDefault
bin
commands
icons
private
settings.json
Shell cwd was reset to /Users/marcus.hinz/claude-projects/ai-control


das passt aber nicht aIC  - ist besitzer des /pool verzeichnisses -> legt dort die PoolCofigs ab sollte auch die entsprechenden Symlinks asnlegen


Bestätigt, du hast recht. Ich sehe es jetzt vollständig:

`~/claude-projects/pool/` ist ein **Git-Repo** und das von aIC verwaltete Sync-Ziel (`poolSyncDir`). Darin liegt **pro Pool ein Unterordner** (`private/`, `apiDefault/`, `24d5f60d…/`) mit den synced Runtime-Daten `projects/`, `todos/`, `history.jsonl` — genau die `SYNCED_RUNTIME`-Liste. Die Gegenrichtung stimmt: in `~/.config/ai-control/pools/private/` stehen die Symlinks `projects`, `todos`, `history.jsonl` zurück auf `pool/private/…`.

Damit war meine vorige Trennung falsch — aIC ist Besitzer von `/pool` und legt dort die Pool-Daten ab **und** setzt die Symlinks (`link_pool_runtime_in`, ausgelöst bei Pool-Anlage/Verlinkung).

Eine Präzisierung zum Wording „PoolConfigs": die eigentliche claude-Config-Dir eines Pools (`.credentials.json`, `.claude.json`, `settings.json`, `sessions/` …) liegt unter `~/.config/ai-control/pools/<id>/`. Im `/pool`-Repo landen davon nur die drei synced Runtime-Artefakte pro Pool — plus die geteilten Ressourcen `CLAUDE.md`, `icons/`, `commands/`, `bin/`, `settings.json`.


was passiert bei Anlage eines Pools konkret?


cd ~/projects/ai-control && grep -n "fn create_.*pool\|fn init_pool_config\|fn write_pool_json\|fn check_new_pool\|fn init_pool_claude_md\|CLAUDE.md\|fn setup_pool\|create_oauth_pool\|create_apikey_pool" src-tauri/src/lib.rs | head -40

Pool-Anlage-Funktionen finden


1159:fn check_new_pool(paths: &Paths, name: &str) -> Result<PathBuf, String> {
1167:fn write_pool_json(dir: &PathBuf, name: &str, credential_type: &str) -> Result<(), String> {
1186:/// (aufgeräumte UI-Defaults + `extra`) und eine CLAUDE.md, die claude als
1187:/// User-Scope liest. CLAUDE.md wird nur angelegt, wenn sie fehlt.
1190:fn init_pool_config(
1207:  let claude_md = dir.join("CLAUDE.md");
1265:/// allow_file), settings.json mit apiKeyHelper-Kette, CLAUDE.md, pool.json.
1268:fn create_apikey_pool_in(
1298:/// Legt einen oauth-Pool an: Grundausstattung (leere settings.json + CLAUDE.md)
1302:fn create_oauth_pool_in(paths: &Paths, name: &str) -> Result<String, String> {
1675:fn create_oauth_pool(name: String) -> Result<String, String> {
1676:  create_oauth_pool_in(&Paths::real(), &name)
1680:fn create_apikey_pool(name: String, key: String, allow_file: bool) -> Result<String, String> {
1681:  create_apikey_pool_in(&Paths::real(), &KeychainStore, &name, &key, allow_file)
2146:      create_oauth_pool,
2147:      create_apikey_pool,
2264:    create_oauth_pool_in(p, name).unwrap()
2268:    create_apikey_pool_in(p, store, name, key, true).unwrap()
2329:    let err = create_apikey_pool_in(&p, &FailStore, "kunde", "sk-1", false).unwrap_err();
2376:    // Grundausstattung: settings.json mit aufgeräumten UI-Defaults + CLAUDE.md,
2383:    assert!(dir.join("CLAUDE.md").is_file());
2391:    fs::write(dir.join("CLAUDE.md"), "meine Vorgaben\n").unwrap();
2392:    // Anlegen darf die vorhandene CLAUDE.md nicht überschreiben.
2396:      fs::read_to_string(dir.join("CLAUDE.md")).unwrap(),
2406:    let err = create_apikey_pool_in(&p, &store, "kunde", "sk-2", true).unwrap_err();
2444:    assert!(create_apikey_pool_in(&p, &map_store(), "kunde", "  ", true).is_err());
2450:    assert!(create_apikey_pool_in(&p, &map_store(), "a/b", "sk-1", true).is_err());


1150	    let pool: Pool =
1151	      serde_json::from_str(&raw).map_err(|e| format!("{}: {e}", cfg_path.display()))?;
1152	    out.push((entry.file_name().to_string_lossy().into_owned(), pool.name));
1153	  }
1154	  Ok(out)
1155	}
1156
1157	/// Prüft den Anzeigenamen (gültig + noch nicht vergeben) und liefert den
1158	/// Ordner für einen neuen Pool: pools/<UUID v4>.
1159	fn check_new_pool(paths: &Paths, name: &str) -> Result<PathBuf, String> {
1160	  check_name(name)?;
1161	  if pool_names(paths)?.iter().any(|(_, n)| n == name) {
1162	    return Err(format!("Pool existiert bereits: {name}"));
1163	  }
1164	  Ok(paths.pool_dir(&uuid::Uuid::new_v4().to_string()))
1165	}
1166
1167	fn write_pool_json(dir: &PathBuf, name: &str, credential_type: &str) -> Result<(), String> {
1168	  fs::create_dir_all(dir).map_err(|e| e.to_string())?;
1169	  let pool = Pool {
1170	    name: name.to_string(),
1171	    credential_type: credential_type.to_string(),
1172	  };
1173	  let raw = serde_json::to_string_pretty(&pool).map_err(|e| e.to_string())?;
1174	  fs::write(dir.join(POOL_FILE), raw + "\n").map_err(|e| e.to_string())
1175	}
1176
1177	fn write_secret_file(path: &PathBuf, content: &str) -> Result<(), String> {
1178	  if let Some(parent) = path.parent() {
1179	    fs::create_dir_all(parent).map_err(|e| e.to_string())?;
1180	  }
1181	  fs::write(path, content).map_err(|e| e.to_string())?;
1182	  fs::set_permissions(path, fs::Permissions::from_mode(0o600)).map_err(|e| e.to_string())
1183	}
1184
1185	/// Grundausstattung eines Pool-Ordners (= CLAUDE_CONFIG_DIR): settings.json
1186	/// (aufgeräumte UI-Defaults + `extra`) und eine CLAUDE.md, die claude als
1187	/// User-Scope liest. CLAUDE.md wird nur angelegt, wenn sie fehlt.
1188	/// Die Prompt-Vorschläge/Rückkehr-Zusammenfassung werden abgeschaltet — sonst
1189	/// erscheint oben die Vorschlagstabelle.
1190	fn init_pool_config(
1191	  dir: &PathBuf,
1192	  extra: serde_json::Value,
1193	) -> Result<(), String> {
1194	  let mut settings = serde_json::json!({
1195	    "promptSuggestionEnabled": false,
1196	    "awaySummaryEnabled": false,
1197	  });
1198	  let base = settings.as_object_mut().unwrap();
1199	  if let Some(obj) = extra.as_object() {
1200	    for (k, v) in obj {
1201	      base.insert(k.clone(), v.clone());
1202	    }
1203	  }
1204	  fs::create_dir_all(dir).map_err(|e| e.to_string())?;
1205	  let raw = serde_json::to_string_pretty(&settings).map_err(|e| e.to_string())?;
1206	  fs::write(dir.join("settings.json"), raw + "\n").map_err(|e| e.to_string())?;
1207	  let claude_md = dir.join("CLAUDE.md");
1208	  if !claude_md.is_file() {
1209	    fs::write(&claude_md, "").map_err(|e| e.to_string())?;

    let pool: Pool =
      serde_json::from_str(&raw).map_err(|e| format!("{}: {e}", cfg_path.display()))?;
    out.push((entry.file_name().to_string_lossy().into_owned(), pool.name));
  }
  Ok(out)
}

/// Prüft den Anzeigenamen (gültig + noch nicht vergeben) und liefert den
/// Ordner für einen neuen Pool: pools/<UUID v4>.
fn check_new_pool(paths: &Paths, name: &str) -> Result<PathBuf, String> {
  check_name(name)?;
  if pool_names(paths)?.iter().any(|(_, n)| n == name) {
    return Err(format!("Pool existiert bereits: {name}"));
  }
  Ok(paths.pool_dir(&uuid::Uuid::new_v4().to_string()))
}

fn write_pool_json(dir: &PathBuf, name: &str, credential_type: &str) -> Result<(), String> {
  fs::create_dir_all(dir).map_err(|e| e.to_string())?;
  let pool = Pool {
    name: name.to_string(),
    credential_type: credential_type.to_string(),
  };
  let raw = serde_json::to_string_pretty(&pool).map_err(|e| e.to_string())?;
  fs::write(dir.join(POOL_FILE), raw + "\n").map_err(|e| e.to_string())
}

fn write_secret_file(path: &PathBuf, content: &str) -> Result<(), String> {
  if let Some(parent) = path.parent() {
    fs::create_dir_all(parent).map_err(|e| e.to_string())?;
  }
  fs::write(path, content).map_err(|e| e.to_string())?;
  fs::set_permissions(path, fs::Permissions::from_mode(0o600)).map_err(|e| e.to_string())
}

/// Grundausstattung eines Pool-Ordners (= CLAUDE_CONFIG_DIR): settings.json
/// (aufgeräumte UI-Defaults + `extra`) und eine CLAUDE.md, die claude als
/// User-Scope liest. CLAUDE.md wird nur angelegt, wenn sie fehlt.
/// Die Prompt-Vorschläge/Rückkehr-Zusammenfassung werden abgeschaltet — sonst
/// erscheint oben die Vorschlagstabelle.
fn init_pool_config(
  dir: &PathBuf,
  extra: serde_json::Value,
) -> Result<(), String> {
  let mut settings = serde_json::json!({
    "promptSuggestionEnabled": false,
    "awaySummaryEnabled": false,
  });
  let base = settings.as_object_mut().unwrap();
  if let Some(obj) = extra.as_object() {
    for (k, v) in obj {
      base.insert(k.clone(), v.clone());
    }
  }
  fs::create_dir_all(dir).map_err(|e| e.to_string())?;
  let raw = serde_json::to_string_pretty(&settings).map_err(|e| e.to_string())?;
  fs::write(dir.join("settings.json"), raw + "\n").map_err(|e| e.to_string())?;
  let claude_md = dir.join("CLAUDE.md");
  if !claude_md.is_file() {
    fs::write(&claude_md, "").map_err(|e| e.to_string())?;


1209	    fs::write(&claude_md, "").map_err(|e| e.to_string())?;
1210	  }
1211	  Ok(())
1212	}
1213
1214	/// Runtime, die pro Pool ins synced Repo gelinkt wird: Transkripte, Todos,
1215	/// Prompt-Historie. (name, ist_ordner)
1216	const SYNCED_RUNTIME: [(&str, bool); 3] =
1217	  [("projects", true), ("todos", true), ("history.jsonl", false)];
1218

    fs::write(&claude_md, "").map_err(|e| e.to_string())?;
  }
  Ok(())
}

/// Runtime, die pro Pool ins synced Repo gelinkt wird: Transkripte, Todos,
/// Prompt-Historie. (name, ist_ordner)
const SYNCED_RUNTIME: [(&str, bool); 3] =
  [("projects", true), ("todos", true), ("history.jsonl", false)];


1294	  }
1295	  Ok(id)
1296	}
1297
1298	/// Legt einen oauth-Pool an: Grundausstattung (leere settings.json + CLAUDE.md)
1299	/// + pool.json. Die Anmeldung macht claude selbst beim ersten Start des Pools
1300	/// (`/login`) und legt den Keychain-Eintrag an — die App speichert keine Tokens.
1301	/// Liefert die Pool-ID.
1302	fn create_oauth_pool_in(paths: &Paths, name: &str) -> Result<String, String> {
1303	  let dir = check_new_pool(paths, name)?;
1304	  init_pool_config(&dir, serde_json::json!({}))?;
1305	  write_pool_json(&dir, name, "oauth")?;
1306	  let id = dir.file_name().unwrap().to_string_lossy().into_owned();
1307	  if pool_sync_dir(paths).is_some() {
1308	    link_pool_runtime_in(paths, &id)?;
1309	  }
1310	  Ok(id)
1311	}
1312
1313	/// Setzt den Anzeigenamen eines Pools — reines pool.json-Update, ID/Ordner
1314	/// (und damit Keychain-Suffix, Symlinks, Zuordnungen) bleiben unverändert.
1315	fn rename_pool_in(paths: &Paths, pool: &str, name: &str) -> Result<(), String> {
1316	  check_name(name)?;
1317	  let current = read_pool(paths, pool)?;
1318	  if pool_names(paths)?.iter().any(|(id, n)| id != pool && n == name) {
1319	    return Err(format!("Pool existiert bereits: {name}"));
1320	  }
1321	  write_pool_json(&paths.pool_dir(pool), name, &current.credential_type)
1322	}
1323

  }
  Ok(id)
}

/// Legt einen oauth-Pool an: Grundausstattung (leere settings.json + CLAUDE.md)
/// + pool.json. Die Anmeldung macht claude selbst beim ersten Start des Pools
/// (`/login`) und legt den Keychain-Eintrag an — die App speichert keine Tokens.
/// Liefert die Pool-ID.
fn create_oauth_pool_in(paths: &Paths, name: &str) -> Result<String, String> {
  let dir = check_new_pool(paths, name)?;
  init_pool_config(&dir, serde_json::json!({}))?;
  write_pool_json(&dir, name, "oauth")?;
  let id = dir.file_name().unwrap().to_string_lossy().into_owned();
  if pool_sync_dir(paths).is_some() {
    link_pool_runtime_in(paths, &id)?;
  }
  Ok(id)
}

/// Setzt den Anzeigenamen eines Pools — reines pool.json-Update, ID/Ordner
/// (und damit Keychain-Suffix, Symlinks, Zuordnungen) bleiben unverändert.
fn rename_pool_in(paths: &Paths, pool: &str, name: &str) -> Result<(), String> {
  check_name(name)?;
  let current = read_pool(paths, pool)?;
  if pool_names(paths)?.iter().any(|(id, n)| id != pool && n == name) {
    return Err(format!("Pool existiert bereits: {name}"));
  }
  write_pool_json(&paths.pool_dir(pool), name, &current.credential_type)
}


Bei Pool-Anlage (Ordner ist immer `~/.config/ai-control/pools/<UUID-v4>`, die ID) — zwei Wege:

**oauth-Pool** (`create_oauth_pool_in`):
1. `check_new_pool` — Name validieren, Kollision prüfen, UUID-Ordner bestimmen.
2. `init_pool_config(dir, {})` — `settings.json` mit UI-Defaults (`promptSuggestionEnabled:false`, `awaySummaryEnabled:false`); leere `CLAUDE.md`, nur falls noch keine da ist.
3. `write_pool_json` — `pool.json` = `{name, credential_type:"oauth"}`.
4. Wenn `poolSyncDir` gesetzt: `link_pool_runtime_in`.
5. Kein Token gespeichert — claude macht `/login` beim ersten Start selbst und legt den Keychain-Eintrag an.

**apikey-Pool** (`create_apikey_pool_in`): wie oben, zusätzlich vor Schritt 2:
- Key trimmen, leer → Abbruch.
- `store.set(id, key)` → Key in Keychain (per ID suffixiert). Schlägt fehl und `allow_file=false` → Abbruch (`KEYCHAIN_UNAVAILABLE`); mit `allow_file` → `apikey`-Datei 0600 im Pool-Ordner.
- `init_pool_config` bekommt hier `{apiKeyHelper: …}` in die `settings.json`.

**`link_pool_runtime_in`** (nur wenn `poolSyncDir` konfiguriert):
- Legt im Sync-Repo `~/claude-projects/pool/<id>/` die drei `SYNCED_RUNTIME`-Artefakte an: `projects/`, `todos/` (Ordner), `history.jsonl` (leere Datei).
- Ersetzt im Pool-Ordner die gleichnamigen Einträge durch Symlinks dorthin (vorhandene echte Inhalte werden dabei verworfen — kein History-Erhalt, bewusst).

**Ergebnis** — `~/.config/ai-control/pools/<id>/` enthält: `settings.json`, `CLAUDE.md`, `pool.json`, ggf. `apikey`, plus Symlinks `projects` / `todos` / `history.jsonl`. Nicht angelegt werden `.credentials.json`, `.claude.json`, `sessions/` usw. — die entstehen erst beim ersten `claude`-Start im Pool.