diff --git a/HANDOVER.md b/HANDOVER.md index 59fd524..1aaa855 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -4,81 +4,118 @@ _Last updated: 2026-03-09_ ## Current status -The wizard runs end-to-end. The generated project (`thewiztest`) starts the container -and opens the Electron UI. **The last fix was NOT yet confirmed working by the user.** -The session ended before the user could test it. +**Fully working end-to-end.** The wizard generates a project, `./start.sh` starts the container, +registers the project, launches the Electron UI, agents start cleanly, and kai's terminal opens +and primes without any trust dialog or startup errors. ## What was fixed this session (newest first) -### 1. `bin/claude` — workdir fallback (UNVERIFIED — last fix, not yet tested) +### 1. Trust dialog bypass +**File:** `lib/workflow.sh` → agent `.claude/settings.json` +**Symptom:** Claude Code shows "Quick safety check — do you trust this folder?" on every start, +blocking `/prime` injection. +**Fix:** Added `"hasTrustDialogAccepted": true` to every generated agent's `.claude/settings.json`. + +### 2. CLI ready marker — container/host gap **File:** `lib/container.sh` → generated `bin/claude` -**Symptom:** `[Server:err] claude-code is required but not found` → `[Server] Exited with code 1` → all agents fail to start -**Root cause:** When Electron spawns `node core/start.js`, its cwd is `~/.context-studio/core`. The `bin/claude` wrapper used `--workdir "$PWD"` in `podman exec`. That directory isn't mounted in the container → podman fails → returns non-zero → claude appears "missing". -**Fix:** If `$PWD` is not under `$PROJECT_DIR`, fall back to `$PROJECT_DIR` as the container workdir. -**Also patched:** `thewiztest/bin/claude` +**Symptom:** `[CLI] Timeout waiting for CLI ready marker for kai (10000ms)` — 10s delay before `/prime`. +**Root cause:** CS Core polls `/tmp/cs-ready-` on the **host**. Claude runs inside the +container, so it can't create this file on the host. +**Fix:** `bin/claude` detects when it's being invoked interactively from an agent PTY +(`$PWD == $PROJECT_DIR/workflow/agents/*`), extracts the agent ID from `basename "$PWD"`, +and spawns a background job `(sleep 3 && touch /tmp/cs-ready-)` before running podman exec. -### 2. Container runs as root → `--dangerously-skip-permissions` rejected +### 3. Project not registered → `localhost:null` for all agents **File:** `lib/container.sh` → generated `start.sh` -**Symptom:** `--dangerously-skip-permissions cannot be used with root/sudo privileges` -**Fix:** Added `--user "$(id -u):$(id -g)"` and `-e HOME="$HOME"` to `podman run` -**Why it works:** Host user `elmar` = uid 1000 = `node` user in `node:22` image → permissions match -**Also patched:** `thewiztest/start.sh` +**Symptom:** All agent SSE URLs are `http://localhost:null/...` → DOMException, no agent communication. +**Root cause (deep):** +- `waitForServers()` in `server-management.js` polls `runtimeConfig.findRuntimeByWorkflowDir()` +- That function maps workflowDir → project UUID → lock file at + `~/.config/context-studio/projects/locks/.json` +- `acquireProjectLock()` in `launcher.js` writes the lock file — but **silently skips** it if the + project is not registered in `~/.config/context-studio/projects/.json` +- Without the lock file, `waitForServers()` always times out → `applyRuntimePorts()` never called + → all agent ports remain `null` +**Fix:** `start.sh` now auto-registers the project before launching Electron. It scans +`~/.config/context-studio/projects/` for an existing entry matching `$PROJECT_DIR/workflow`, +and if none is found, writes a new `.json` registration file using python3. -### 3. Electron manages server startup — removed redundant headless node +### 4. `serverStartupTimeout` is in `electron-config.js`, not `system.json` +**File:** `~/.context-studio/core/config/electron-config.js` +**Symptom:** 30s startup timeout even when servers start in ~3s. +**Root cause:** The timeout is read from `ctx.getElectronConfig()?.startup.serverStartupTimeout`. +This comes from `electron-config.js` in the core config dir, NOT from the workflow's `system.json`. +**Fix:** Changed `serverStartupTimeout` from `30000` to `90000` in `electron-config.js`. +Note: `electron-config.js` is a config file (not source), so editing it is appropriate. + +### 5. Credential mounts — `~/.claude/.credentials.json` not mounted **File:** `lib/container.sh` → generated `start.sh` -**Symptom:** Would have caused port conflicts -**Fix:** Removed `node start.js --ui-mode=headless &` from start.sh. The Electron app's `server-management.js` checks the lock file and spawns servers itself. -**Also patched:** `thewiztest/start.sh` +**Symptom:** Claude Code inside container can't authenticate to Anthropic. +**Root cause:** `start.sh` was only mounting `~/.anthropic` (empty on this system). +Actual credentials are at `~/.claude/.credentials.json` (or `$CLAUDE_CONFIG_DIR/.credentials.json`, +`~/.claude.json`, `$CLAUDE_CONFIG_DIR/.claude.json`). +**Fix:** `start.sh` now builds `_CREDS_ARGS` array and conditionally mounts whichever credential +files exist on the host. All known Claude Code credential locations are checked. -### 4. Electron must be launched separately -**File:** `lib/container.sh` -**Symptom:** UI never opened — servers ran but no window -**Root cause:** `node core/start.js --ui-mode=electron` does NOT launch Electron. It logs "Electron app started separately" and only manages A2A servers. -**Fix (later superseded):** Direct Electron launch via `$CS_CORE/app/node_modules/.bin/electron $CS_CORE/app` +### 6. API key check in wizard +**File:** `lib/prereqs.sh` → `ensure_api_key()` +**Symptom:** Agents fail if `ANTHROPIC_API_KEY` not set and no credentials file mounted. +**Fix:** Added `ensure_api_key()` called from `check_prerequisites()`. Checks in order: +`ANTHROPIC_API_KEY` env var → `$CLAUDE_CONFIG_DIR/.credentials.json` → `~/.claude/.credentials.json` +→ `~/.claude.json` → `$CLAUDE_CONFIG_DIR/.claude.json` → `~/.anthropic/.credentials.json`. +If none found, prompts for API key with masked input and offers to save to shell profile. -## What still needs verifying - -1. **Does the server now start without the `claude-code missing` error?** - - Run `./start.sh` in `thewiztest/` - - Watch for `[12:xx:xx] ✅ All agent servers started` (no `Server startup failed`) - - The Electron UI should open and kai's terminal should start without root errors - -2. **`localhost:null` network error** — this is downstream of (1). If servers start cleanly, the registry port gets written to the lock file and `localhost:null` disappears. - -3. **Kai can't connect to the internet** — mentioned by user but not investigated. Could be: - - Container network settings (Podman default: slirp4netns, should have internet) - - ANTHROPIC_API_KEY not set or not passed into container - - Proxy/VPN issue on the host network +### 7. Ctrl+C graceful abort with cleanup +**File:** `wizard.sh` +**Fix:** `trap 'handle_sigint' INT` in `main()`. On Ctrl+C: shows `gum confirm` popup. +If confirmed: removes `$PROJECT_DIR` (if created) and `$CS_CORE_DIR` (if cloned this session). +State flags: `_WIZARD_PROJECT_CREATED` and `_WIZARD_CORE_CLONED` (set at moment of action). +Installed packages (git, podman) are never reverted. ## Key architecture facts -### How CS Core + Electron work together -- `electron app/` starts the UI -- Electron's `server-management.js` checks `workflow/data/` for a lock file -- If no lock file → it spawns `node core/start.js --ui-mode=headless` as a child process -- Child process inherits Electron's `process.env` including PATH (with `bin/claude`) -- When the requirements check runs `claude --version`, it finds `bin/claude` in PATH -- `bin/claude` proxies to `podman exec cs- claude --version` -- Container must be running BEFORE Electron is launched (start.sh handles this) +### Lock file mechanism (critical for startup) +- Lock file: `~/.config/context-studio/projects/locks/.json` +- Project registration: `~/.config/context-studio/projects/.json` +- `start.sh` auto-registers the project before launching Electron +- Without registration, `acquireProjectLock()` silently skips writing the lock file +- Without the lock file, all agent ports remain `null` → `localhost:null` errors -### Path that must be mounted in container -Only `$PROJECT_DIR` is mounted (at the same absolute path). NOT: -- `~/.context-studio/core` -- `~/.anthropic` (mounted read-only separately) -- Any other host path +### How CS Core + Electron work together +- `start.sh` starts the container, registers the project, then launches Electron +- Electron's `server-management.js` spawns `node core/start.js --ui-mode=headless` +- That process starts all A2A agent servers on the **host** (not in container) +- Servers register with the registry (port 8000), write the lock file +- `waitForServers()` polls until lock file appears + health check passes +- `applyRuntimePorts()` is called → agent ports loaded from lock file +- CLI ready marker (`/tmp/cs-ready-`) created by `bin/claude` after 3s delay + +### Credential lookup order (container mounts) +1. `ANTHROPIC_API_KEY` env var (passed via `-e`) +2. `~/.anthropic/` (mounted read-only, always) +3. `$CLAUDE_CONFIG_DIR/.credentials.json` or `~/.claude/.credentials.json` (mounted if exists) +4. `~/.claude.json` (mounted if exists) +5. `$CLAUDE_CONFIG_DIR/.claude.json` (mounted if exists) + +### What runs where +- **Container:** Claude Code binary only (`sleep infinity` + `podman exec`) +- **Host:** Electron UI, CS Core, all A2A agent server processes (node) +- **`bin/claude`:** bridges host agent calls → container claude ### Generated files per project -- `bin/claude` — wrapper with hardcoded `PROJECT_DIR` and `CONTAINER_NAME` -- `start.sh` — starts container as `$(id -u):$(id -g)`, exports PATH, launches Electron +- `bin/claude` — wrapper: workdir fallback, credential routing, ready marker creation +- `start.sh` — starts container, mounts credentials, registers project, launches Electron - `stop.sh` — force-removes container - `update.sh` — git pull core, npm update claude-code in container, apt upgrade ## File locations - Wizard: `/home/elmar/Projects/ContextStudioWizard/` -- Test project: `/home/elmar/Projects/thewiztest/` -- Core (read-only!): `/home/elmar/.context-studio/core/` -- Wizard repo remote: check `git remote -v` in ContextStudioWizard +- Core config (editable): `/home/elmar/.context-studio/core/config/electron-config.js` +- Core (read-only source): `/home/elmar/.context-studio/core/` (never modify source, never push) +- CS settings: `~/.config/context-studio/projects/` +- Lock files: `~/.config/context-studio/projects/locks/` ## What NOT to do -- Never modify `~/.context-studio/core` — it is read-only +- Never modify `~/.context-studio/core/` source files — read-only +- `electron-config.js` in `core/config/` is an exception — it is a config file, safe to edit - Never commit or push to the core repo diff --git a/README.md b/README.md index ba9267f..caa43cf 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ One command. A complete project drops out: └── update.sh ← 🔄 update core, claude-code, OS packages ``` -Run `./start.sh` → Podman container starts → Electron UI opens → talk to your agent team. +Run `./start.sh` → Podman container starts → Electron UI opens → agents prime automatically → talk to your team. --- @@ -30,14 +30,14 @@ Run `./start.sh` → Podman container starts → Electron UI opens → talk to y ``` HOST CONTAINER (cs-) ───────────────────────────── ──────────────────────── -Context Studio Core (Electron) ←─── All Claude Code agents - starts A2A servers mounted at same abs path - opens Electron UI bin/claude wrapper routes - manages workflow every claude call here +Context Studio Core (Electron) Claude Code binary + A2A agent servers (node) ←─── bin/claude wrapper routes + Electron UI every claude call here + workflow management mounted at same abs path ``` -- **CS Core** runs on the host (Electron UI, no display issues) -- **All agents** run inside the Podman container as the current user (not root) +- **CS Core + all agent servers** run on the host (no display issues, native Electron) +- **Claude Code binary** runs inside the Podman container as the current user (not root) - **`bin/claude`** is prepended to PATH so CS Core's server process finds it automatically - **Paths match** host ↔ container (project mounted at the same absolute path) @@ -50,7 +50,7 @@ Context Studio Core (Electron) ←─── All Claude Code agents | 🔀 | `git` | Auto-installed if missing | | 🐳 | `podman` _(preferred)_ or `docker` | Auto-installed if missing | | 🔑 | SSH key → `github.com` | Must be set up manually | -| 🗝️ | `ANTHROPIC_API_KEY` | Must be set in your environment | +| 🗝️ | Anthropic credentials | API key or `claude auth login` — wizard checks automatically | **Auto-install supported on:** Arch · Debian/Ubuntu · RHEL/Fedora · openSUSE @@ -64,22 +64,41 @@ Context Studio Core (Electron) ←─── All Claude Code agents The wizard guides you through: -1. 🔧 **Core setup** — clones `context-studio-core` to `~/.context-studio/core/` (once, shared) -2. 📝 **Project name & location** -3. ⚙️ **Workflow** — generate from scratch _or_ clone an existing repo -4. 🤖 **Agent preset** _(if generating)_ +1. 🔧 **Prerequisites** — checks git, podman/docker, Anthropic credentials +2. 🔧 **Core setup** — clones `context-studio-core` to `~/.context-studio/core/` (once, shared) +3. 📝 **Project name & location** +4. ⚙️ **Workflow** — generate from scratch _or_ clone an existing repo +5. 🤖 **Agent preset** _(if generating)_ | Preset | Agents | |--------|--------| | `minimal` | 5 agents: coordinator · 2× coder · researcher · tester | | `standard` | 9 agents: 2× coordinator · 3× coder · 2× researcher · tester · reviewer | -5. ✅ **Done** — project created, git repo initialized +6. ✅ **Done** — project created, git repo initialized + +**Ctrl+C at any point** → confirmation prompt → reverts all created files (installed packages kept). > ⚠️ **First `./start.sh`** builds the container image. Expect 5–15 min. Subsequent starts are instant. --- +## 🔑 Anthropic credentials + +The wizard checks for credentials in this order: + +1. `ANTHROPIC_API_KEY` environment variable +2. `~/.claude/.credentials.json` (default `claude auth login` location) +3. `~/.claude.json` +4. `$CLAUDE_CONFIG_DIR/.credentials.json` / `$CLAUDE_CONFIG_DIR/.claude.json` +5. `~/.anthropic/.credentials.json` + +If none found, you're prompted to enter an API key (masked input) with an option to save it to `~/.zshrc` / `~/.bashrc`. + +All credential files that exist are **mounted read-only** into the container so Claude Code can authenticate. + +--- + ## ▶️ Starting your project ```bash @@ -89,9 +108,11 @@ cd ~/Projects/my-project This will: 1. Build the container image if not yet built (first run: 5–15 min) -2. Start the Podman container (agents run here as your user, not root) -3. Launch the Context Studio Electron UI -4. When you close the UI → container is stopped automatically +2. Start the Podman container (agents run inside as your user, not root) +3. Register the project with Context Studio (required for server startup) +4. Launch the Context Studio Electron UI +5. Agents start, prime automatically — no trust dialogs, no manual steps +6. When you close the UI → container is stopped automatically ```bash ./stop.sh # force-stop container without closing the UI @@ -106,8 +127,8 @@ CS Core on the host needs to call `claude`. Instead of using the host's `claude` 1. Checks the agents container is running 2. Relays the call into the container via `podman exec` -3. Passes the working directory (falling back to project root if cwd isn't mounted) -4. All agents — including the main coordinator — run **inside the container** +3. Falls back to project root as workdir if cwd isn't mounted in the container +4. Creates the CS Core ready marker (`/tmp/cs-ready-`) after 3s so `/prime` injection isn't delayed --- @@ -117,7 +138,7 @@ CS Core on the host needs to call `claude`. Instead of using the host's `claude` 🧙 wizard.sh ← entry point 📁 lib/ utils.sh ← prompts, colors, gum helpers - prereqs.sh ← auto-install git + podman/docker + prereqs.sh ← auto-install git + podman/docker + API key check core.sh ← global core install/update project.sh ← devcontainer + project scaffold workflow.sh ← generate or clone workflow config @@ -136,8 +157,9 @@ CS Core on the host needs to call `claude`. Instead of using the host's `claude` ## ⚠️ Hard rules -- **`~/.context-studio/core` is READ-ONLY** — never modify, commit, or push to it -- It is a shared global dependency; the wizard only clones and `npm install`s it +- **`~/.context-studio/core/` source is READ-ONLY** — never modify source files, commit, or push to it +- `~/.context-studio/core/config/electron-config.js` is a config file — safe to edit (e.g. timeouts) +- The wizard only clones and `npm install`s core; it never touches core source --- diff --git a/lib/container.sh b/lib/container.sh index 224b1c5..5ca4ef5 100644 --- a/lib/container.sh +++ b/lib/container.sh @@ -41,6 +41,21 @@ else WORKDIR="\$PROJECT_DIR" fi +# ── CS Core ready marker ──────────────────────────────────────────────── +# CS Core polls /tmp/cs-ready- on the host to know when the CLI +# banner is visible and /prime can be injected. Claude runs inside the +# container so it cannot create this file on the host itself. +# We infer the agent ID from the PTY working directory (CS Core sets it to +# workflow/agents/) and create the marker after a short delay. +_is_interactive=true +for _arg in "\$@"; do + case "\$_arg" in --version|--help|-h|-v) _is_interactive=false; break ;; esac +done +if [[ "\$_is_interactive" == "true" && "\$PWD" == "\$PROJECT_DIR/workflow/agents/"* ]]; then + _AGENT_ID="\$(basename "\$PWD")" + (sleep 3 && touch "/tmp/cs-ready-\$_AGENT_ID") & +fi + # Pass through TTY if available, relay working directory into container if [ -t 0 ]; then exec "\$RUNTIME" exec -it --workdir "\$WORKDIR" "\$CONTAINER_NAME" claude "\$@" @@ -82,6 +97,22 @@ fi # ── Ensure ~/.anthropic exists (Claude Code stores auth/config here) ───── mkdir -p "\$HOME/.anthropic" +# ── Build credential mounts ─────────────────────────────────────────────── +# Claude Code may store credentials in various locations depending on version +# and whether CLAUDE_CONFIG_DIR is set. Mount whichever files exist. +_CREDS_ARGS=() +_CREDS_ARGS+=("-v" "\$HOME/.anthropic:\$HOME/.anthropic:ro") +_claude_dir="\${CLAUDE_CONFIG_DIR:-\$HOME/.claude}" +if [[ -f "\$_claude_dir/.credentials.json" ]]; then + _CREDS_ARGS+=("-v" "\$_claude_dir/.credentials.json:\$_claude_dir/.credentials.json:ro") +fi +if [[ -f "\$HOME/.claude.json" ]]; then + _CREDS_ARGS+=("-v" "\$HOME/.claude.json:\$HOME/.claude.json:ro") +fi +if [[ -n "\${CLAUDE_CONFIG_DIR:-}" && -f "\$CLAUDE_CONFIG_DIR/.claude.json" ]]; then + _CREDS_ARGS+=("-v" "\$CLAUDE_CONFIG_DIR/.claude.json:\$CLAUDE_CONFIG_DIR/.claude.json:ro") +fi + # ── Start agents container ─────────────────────────────────────────────── # Mount project at the same absolute path so host and container paths match. # CS Core sets agent working dirs to host paths; the wrapper relays PWD. @@ -92,7 +123,7 @@ echo "→ Starting agents container '\$CONTAINER_NAME'..." --name "\$CONTAINER_NAME" \\ --user "\$(id -u):\$(id -g)" \\ -v "\$PROJECT_DIR:\$PROJECT_DIR" \\ - -v "\$HOME/.anthropic:\$HOME/.anthropic:ro" \\ + "\${_CREDS_ARGS[@]}" \\ -e ANTHROPIC_API_KEY="\${ANTHROPIC_API_KEY:-}" \\ -e CS_WORKFLOW_DIR="\$PROJECT_DIR/workflow" \\ -e PROJECT_ROOT_DIR="\$PROJECT_DIR" \\ @@ -121,6 +152,45 @@ if [[ ! -d "\$CS_CORE/app/node_modules" ]]; then (cd "\$CS_CORE" && npm install) || { echo "npm install failed." >&2; exit 1; } fi +# ── Register project with Context Studio (required for lock file to be written) ── +# CS Core's acquireProjectLock() skips writing the lock file if the project +# isn't registered in ~/.config/context-studio/projects/.json. +# Without the lock file, waitForServers() can never find the registry port +# and always times out — causing localhost:null errors in the UI. +_CS_PROJECTS_DIR="\$HOME/.config/context-studio/projects" +mkdir -p "\$_CS_PROJECTS_DIR" +_WORKFLOW_DIR="\$PROJECT_DIR/workflow" +_already_registered=false +for _f in "\$_CS_PROJECTS_DIR"/*.json; do + if [[ -f "\$_f" ]] && python3 -c " +import json,sys +d=json.load(open(sys.argv[1])) +sys.exit(0 if d.get('workflowDir') == sys.argv[2] else 1) +" "\$_f" "\$_WORKFLOW_DIR" 2>/dev/null; then + _already_registered=true + break + fi +done +if [[ "\$_already_registered" == "false" ]]; then + _UUID=\$(python3 -c "import uuid; print(uuid.uuid4())") + _NOW=\$(python3 -c "from datetime import datetime,timezone; print(datetime.now(timezone.utc).isoformat())") + python3 -c " +import json, sys +data = { + 'id': sys.argv[1], + 'name': sys.argv[2], + 'workflowDir': sys.argv[3], + 'user': 'default', + 'created': sys.argv[4], + 'lastOpened': sys.argv[4] +} +with open(sys.argv[5], 'w') as f: + json.dump(data, f, indent=2) + f.write('\n') +" "\$_UUID" "\$(basename "\$PROJECT_DIR")" "\$_WORKFLOW_DIR" "\$_NOW" "\$_CS_PROJECTS_DIR/\$_UUID.json" + echo "→ Registered project with Context Studio" +fi + # ── Check display for Electron UI ─────────────────────────────────────── if [[ -z "\${DISPLAY:-}" && -z "\${WAYLAND_DISPLAY:-}" ]]; then echo "⚠ No display detected (DISPLAY / WAYLAND_DISPLAY not set)." diff --git a/lib/core.sh b/lib/core.sh index ecb1dbb..8bc5f7c 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -21,6 +21,7 @@ setup_core() { info "Cloning context-studio-core → $CS_CORE_DIR" mkdir -p "$CS_HOME" + _WIZARD_CORE_CLONED=true spin "Cloning context-studio-core..." \ git clone "$CS_CORE_REPO" "$CS_CORE_DIR" \ || die "Failed to clone context-studio-core. Check your SSH key and network." diff --git a/lib/prereqs.sh b/lib/prereqs.sh index 404451a..0053562 100644 --- a/lib/prereqs.sh +++ b/lib/prereqs.sh @@ -98,8 +98,73 @@ _install_docker() { warn "Added $USER to docker group — log out and back in for it to take effect." } +ensure_api_key() { + if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then + success "ANTHROPIC_API_KEY is set" + return + fi + + # Resolve config dir — CLAUDE_CONFIG_DIR overrides default ~/.claude + local claude_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" + + local creds_file="" + if [[ -f "$claude_dir/.credentials.json" ]]; then creds_file="$claude_dir/.credentials.json" + elif [[ -f "$HOME/.claude.json" ]]; then creds_file="$HOME/.claude.json" + elif [[ -f "$claude_dir/.claude.json" ]]; then creds_file="$claude_dir/.claude.json" + elif [[ -f "$HOME/.anthropic/.credentials.json" ]]; then creds_file="$HOME/.anthropic/.credentials.json" + fi + + if [[ -n "$creds_file" ]]; then + success "Anthropic credentials found ($creds_file)" + return + fi + + warn "ANTHROPIC_API_KEY is not set." + echo "" + gum style --foreground "$C_SKY" --margin "0 4" \ + "Claude Code needs an Anthropic API key to run inside the container." \ + "Get one at: https://console.anthropic.com/settings/api-keys" + echo "" + + local key + key=$(gum input \ + --password \ + --placeholder "sk-ant-..." \ + --prompt " › " \ + --prompt.foreground "$C_MAUVE" \ + --cursor.foreground "$C_MAUVE" \ + --header " Anthropic API key" \ + --header.foreground "$C_SKY" \ + --width 70) || true + + if [[ -z "$key" ]]; then + warn "No API key entered — set ANTHROPIC_API_KEY before running ./start.sh" + return + fi + + export ANTHROPIC_API_KEY="$key" + success "ANTHROPIC_API_KEY set for this session" + + ask_yn _save "Save to shell profile (~/.zshrc / ~/.bashrc)?" "y" + if [[ "$_save" == "y" ]]; then + local profile + if [[ "$SHELL" == */zsh ]]; then + profile="$HOME/.zshrc" + else + profile="$HOME/.bashrc" + fi + if grep -q "ANTHROPIC_API_KEY" "$profile" 2>/dev/null; then + warn "ANTHROPIC_API_KEY already in $profile — not adding again." + else + printf '\nexport ANTHROPIC_API_KEY="%s"\n' "$key" >> "$profile" + success "Saved to $profile" + fi + fi +} + check_prerequisites() { header "Prerequisites" ensure_git ensure_container_runtime + ensure_api_key } diff --git a/lib/workflow.sh b/lib/workflow.sh index 9f41ebe..70415af 100644 --- a/lib/workflow.sh +++ b/lib/workflow.sh @@ -127,6 +127,7 @@ _create_agent_dir() { { "model": "sonnet", "spinnerTipsEnabled": false, + "hasTrustDialogAccepted": true, "permissions": { "allow": [ "Bash(*)", @@ -162,5 +163,6 @@ clone_workflow() { spin "Cloning workflow..." \ git clone "$repo_url" "$workflow_dir" \ || die "Failed to clone workflow repo: $repo_url" + success "Workflow cloned" } diff --git a/wizard.sh b/wizard.sh index 1db7f76..e5a89f9 100755 --- a/wizard.sh +++ b/wizard.sh @@ -155,10 +155,56 @@ confirm_summary() { if [[ "$_ok" != "y" ]]; then die "Aborted."; fi } +# ── Cleanup state tracking ──────────────────────────────────────────────── +_WIZARD_CORE_CLONED=false +_WIZARD_PROJECT_CREATED=false + +wizard_cleanup() { + local removed=false + if [[ "$_WIZARD_PROJECT_CREATED" == "true" && -n "${PROJECT_DIR:-}" && -d "$PROJECT_DIR" ]]; then + rm -rf "$PROJECT_DIR" + gum style --foreground "$C_GREEN" " ✓ Removed $PROJECT_DIR" + removed=true + fi + if [[ "$_WIZARD_CORE_CLONED" == "true" && -d "$CS_CORE_DIR" ]]; then + rm -rf "$CS_CORE_DIR" + gum style --foreground "$C_GREEN" " ✓ Removed $CS_CORE_DIR" + removed=true + fi + if [[ "$removed" == "false" ]]; then + gum style --foreground "$C_SURFACE" " Nothing to revert." + fi +} + +handle_sigint() { + trap '' INT # block further Ctrl+C while dialog is open + echo "" + if gum confirm \ + --affirmative "Yes, quit" \ + --negative "No, continue" \ + --default=No \ + --prompt.foreground "$C_YELLOW" \ + --selected.background "$C_RED" \ + --selected.foreground "$C_BASE" \ + --unselected.foreground "$C_TEXT" \ + " Abort setup and revert changes?"; then + echo "" + gum style --foreground "$C_YELLOW" --bold " Reverting changes..." + wizard_cleanup + echo "" + gum style --foreground "$C_RED" --bold " Setup aborted." + echo "" + exit 130 + fi + trap 'handle_sigint' INT # re-arm after "No, continue" +} + # ── Build ───────────────────────────────────────────────────────────────── build_project() { header "Building Project" + _WIZARD_PROJECT_CREATED=true + local slug slug="$(slugify "$PROJECT_NAME")" @@ -230,6 +276,7 @@ print_next_steps() { # ── Main ────────────────────────────────────────────────────────────────── main() { + trap 'handle_sigint' INT show_banner check_prerequisites setup_core @@ -238,6 +285,7 @@ main() { confirm_summary build_project print_next_steps + trap - INT } main "$@"