Compare commits

...

4 commits

Author SHA1 Message Date
ab7b777ced Add handover doc, update README with Option B architecture
README: rewritten to reflect actual Option B architecture (Electron on
host, agents in container), correct start.sh usage, bin/claude routing,
and portability. Remove outdated manual docker run instructions.

HANDOVER.md: documents all fixes made this session, what is unverified,
what still needs testing (claude-code check, localhost:null, network),
and key architecture facts for the next session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:45:37 +01:00
16c0053d68 Fix: bin/claude fallback workdir when cwd is outside mounted project
When CS Core's A2A server process (cwd = ~/.context-studio/core) runs
'claude --version' to check requirements, bin/claude passed --workdir $PWD
to podman exec. But the core dir is not mounted in the container, so
podman fails → check reports claude-code missing → server exits code 1.

Fix: if $PWD is not under $PROJECT_DIR (the only mounted path), fall back
to $PROJECT_DIR as the container workdir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:43:30 +01:00
0028ef4262 Fix: run container as host user, let Electron manage A2A servers
Two fixes:
1. Add --user $(id -u):$(id -g) and -e HOME to container run so Claude
   Code doesn't run as root (--dangerously-skip-permissions is rejected
   by claude when running as root/sudo).

2. Remove redundant `node start.js --ui-mode=headless` from start.sh.
   The Electron app already manages server startup via server-management.js
   (lock file check → spawn headless servers → wait for health). Running
   a second server process causes port conflicts and double-start noise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:34:42 +01:00
b73b2ca7d8 Fix: launch Electron UI separately after A2A servers start
CS Core's start.js only starts A2A servers in electron mode — it does
NOT launch the Electron app itself (it says 'Electron app started
separately'). Switch to --ui-mode=headless for the background servers,
then explicitly launch electron from app/node_modules/.bin/electron.
Wait for registry /health before opening UI. Shut down servers when
Electron window is closed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:24:38 +01:00
3 changed files with 181 additions and 44 deletions

84
HANDOVER.md Normal file
View file

@ -0,0 +1,84 @@
# Handover — Context Studio Wizard
_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.
## What was fixed this session (newest first)
### 1. `bin/claude` — workdir fallback (UNVERIFIED — last fix, not yet tested)
**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`
### 2. Container runs as root → `--dangerously-skip-permissions` rejected
**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`
### 3. Electron manages server startup — removed redundant headless node
**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`
### 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`
## 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
## 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-<slug> claude --version`
- Container must be running BEFORE Electron is launched (start.sh handles this)
### 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
### 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
- `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
## What NOT to do
- Never modify `~/.context-studio/core` — it is read-only
- Never commit or push to the core repo

108
README.md
View file

@ -6,18 +6,40 @@
## ✨ What it does
One command. One conversation. A complete project drops out:
One command. A complete project drops out:
```
~/.context-studio/core/ ← 🔧 installed once, shared globally
~/projects/my-project/
├── .devcontainer/ ← 🐳 Node 22 · Rust · Claude Code · Electron deps
~/Projects/my-project/
├── .devcontainer/ ← 🐳 Node 22 · Rust · Claude Code
├── workflow/ ← 🤖 agents, roles, A2A config, project docs
└── src/ ← 💻 your code
├── src/ ← 💻 your code
├── bin/claude ← 🔀 wrapper: routes claude → container
├── start.sh ← ▶️ start container + Context Studio UI
├── stop.sh ← ⏹️ stop the agents container
└── update.sh ← 🔄 update core, claude-code, OS packages
```
Open in devcontainer → start Context Studio → talk to your agent team.
Run `./start.sh` → Podman container starts → Electron UI opens → talk to your agent team.
---
## 🏗️ Architecture (Option B)
```
HOST CONTAINER (cs-<project>)
───────────────────────────── ────────────────────────
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
```
- **CS Core** runs on the host (Electron UI, no display issues)
- **All agents** run 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)
---
@ -32,8 +54,6 @@ Open in devcontainer → start Context Studio → talk to your agent team.
**Auto-install supported on:** Arch · Debian/Ubuntu · RHEL/Fedora · openSUSE
> The wizard detects your distro and installs `git` and `podman`/`docker` automatically if they are missing. SSH key and API key must be set up by you.
---
## 🚀 Usage
@ -44,57 +64,50 @@ Open in devcontainer → start Context Studio → talk to your agent team.
The wizard guides you through:
1. 🔧 **Core setup** — clones `context-studio-core` to `~/.context-studio/core/` (once)
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)_
| Preset | Agents |
|--------|--------|
| `minimal` | 🎯 coordinator · 2× coder · researcher · tester |
| `standard` | 🎯🎯 2× coordinator · 3× coder · 2× researcher · tester · reviewer |
| `minimal` | 5 agents: coordinator · 2× coder · researcher · tester |
| `standard` | 9 agents: 2× coordinator · 3× coder · 2× researcher · tester · reviewer |
5. ✅ **Done** — git repo initialized, devcontainer ready
5. ✅ **Done** — project created, git repo initialized
> ⚠️ **First `./start.sh`** builds the container image. Expect 515 min. Subsequent starts are instant.
---
## 📂 Open the project
## ▶️ Starting your project
**🖥️ VS Code**
```bash
code ~/projects/my-project # → "Reopen in Container"
cd ~/Projects/my-project
./start.sh
```
**⌨️ CLI**
```bash
cd ~/projects/my-project
docker build -t my-project .devcontainer/
docker run -it --rm \
-v "$(pwd)":/workspace \
-v "$HOME/.context-studio/core":/opt/context-studio/core \
-e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
my-project bash
```
This will:
1. Build the container image if not yet built (first run: 515 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
**▶️ Start Context Studio** _(inside container)_
```bash
node $CS_CORE_DIR/core/start.js # Electron UI
node $CS_CORE_DIR/core/start.js --ui-mode=headless # servers only
./stop.sh # force-stop container without closing the UI
./update.sh # update core, claude-code inside container, OS packages
```
---
## 🌍 Mobility
## 🔄 How `bin/claude` works
Commit your project. On any machine:
CS Core on the host needs to call `claude`. Instead of using the host's `claude`, `start.sh` prepends `bin/claude` to `PATH`. This wrapper:
```bash
git clone <your-project-repo>
cd my-project
code . # done
```
> Core is re-cloned automatically on first run. Only `ANTHROPIC_API_KEY` is needed on the host.
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**
---
@ -103,10 +116,12 @@ code . # done
```
🧙 wizard.sh ← entry point
📁 lib/
utils.sh ← prompts, colors, helpers
utils.sh ← prompts, colors, gum helpers
prereqs.sh ← auto-install git + podman/docker
core.sh ← global core install/update
project.sh ← devcontainer + project scaffold
workflow.sh ← generate or clone workflow config
container.sh ← generates start/stop/update/bin/claude
📁 templates/
Dockerfile ← Node 22 + Rust + build tools + Claude Code
devcontainer.json ← mounts, env vars, VS Code extensions
@ -116,3 +131,22 @@ code . # done
📁 roles/ ← coordinator, coder, researcher, tester, reviewer
📁 hooks/rules/ ← per-role tool restrictions
```
---
## ⚠️ 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
---
## 🌍 Portability
Commit your project. On any machine:
```bash
git clone <your-project-repo>
cd my-project
./start.sh # wizard re-runs if core missing; image builds on first run
```

View file

@ -17,6 +17,7 @@ generate_container_scripts() {
# Claude Code wrapper — runs claude inside the agents container.
# CS Core on the host calls this instead of the real claude binary.
CONTAINER_NAME="${container_name}"
PROJECT_DIR="${project_dir}"
RUNTIME="\$(command -v podman || command -v docker || true)"
if [[ -z "\$RUNTIME" ]]; then
@ -30,11 +31,21 @@ if ! "\$RUNTIME" container inspect "\$CONTAINER_NAME" --format '{{.State.Running
exit 1
fi
# Use PWD as workdir only if it is inside the mounted project tree.
# When CS Core's server process calls claude --version its cwd is the
# core directory (~/.context-studio/core) which is NOT mounted, causing
# podman exec --workdir to fail. Fall back to PROJECT_DIR in that case.
if [[ "\$PWD" == "\$PROJECT_DIR"* ]]; then
WORKDIR="\$PWD"
else
WORKDIR="\$PROJECT_DIR"
fi
# Pass through TTY if available, relay working directory into container
if [ -t 0 ]; then
exec "\$RUNTIME" exec -it --workdir "\$PWD" "\$CONTAINER_NAME" claude "\$@"
exec "\$RUNTIME" exec -it --workdir "\$WORKDIR" "\$CONTAINER_NAME" claude "\$@"
else
exec "\$RUNTIME" exec -i --workdir "\$PWD" "\$CONTAINER_NAME" claude "\$@"
exec "\$RUNTIME" exec -i --workdir "\$WORKDIR" "\$CONTAINER_NAME" claude "\$@"
fi
WRAPPER
chmod +x "$project_dir/bin/claude"
@ -74,14 +85,18 @@ mkdir -p "\$HOME/.anthropic"
# ── 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.
# Run as uid/gid of the current user so Claude Code doesn't run as root
# (--dangerously-skip-permissions is blocked when running as root).
echo "→ Starting agents container '\$CONTAINER_NAME'..."
"\$RUNTIME" run -d \\
--name "\$CONTAINER_NAME" \\
--user "\$(id -u):\$(id -g)" \\
-v "\$PROJECT_DIR:\$PROJECT_DIR" \\
-v "\$HOME/.anthropic:\$HOME/.anthropic:ro" \\
-e ANTHROPIC_API_KEY="\${ANTHROPIC_API_KEY:-}" \\
-e CS_WORKFLOW_DIR="\$PROJECT_DIR/workflow" \\
-e PROJECT_ROOT_DIR="\$PROJECT_DIR" \\
-e HOME="\$HOME" \\
"\$IMAGE_NAME" \\
sleep infinity
@ -113,17 +128,21 @@ if [[ -z "\${DISPLAY:-}" && -z "\${WAYLAND_DISPLAY:-}" ]]; then
fi
# ── Put claude wrapper first on PATH ────────────────────────────────────
# Electron inherits this PATH and passes it to the A2A server it spawns,
# so the servers can locate the bin/claude wrapper inside the container.
export PATH="\$PROJECT_DIR/bin:\$PATH"
# ── Launch CS Core on the host ───────────────────────────────────────────
echo "→ Starting Context Studio Core..."
# ── Launch Context Studio (Electron manages A2A server startup) ──────────
# The Electron app checks for a lock file; if servers aren't running it
# spawns core/start.js --ui-mode=headless internally and waits for health.
echo "→ Launching Context Studio..."
CS_CORE_DIR="\$CS_CORE" \\
CS_WORKFLOW_DIR="\$PROJECT_DIR/workflow" \\
PROJECT_ROOT_DIR="\$PROJECT_DIR" \\
node "\$CS_CORE/core/start.js"
"\$CS_CORE/app/node_modules/.bin/electron" "\$CS_CORE/app"
# ── CS Core exited — stop container ─────────────────────────────────────
echo "→ Stopping agents container..."
# ── Electron closed — stop container ─────────────────────────────────────
echo "→ UI closed. Stopping agents container..."
"\$RUNTIME" rm -f "\$CONTAINER_NAME" 2>/dev/null || true
START
chmod +x "$project_dir/start.sh"