Skip to content

Commit d9621e5

Browse files
centdixclaude
andcommitted
docs: rewrite README to reflect actual project state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5234d6e commit d9621e5

1 file changed

Lines changed: 98 additions & 56 deletions

File tree

README.md

Lines changed: 98 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,121 @@
11
# wmdev
22

3-
Web-based dashboard for managing Git worktrees with integrated terminals and AI agent support. Create, monitor, and interact with multiple isolated development environments — each running its own AI coding agent (Claude or Codex), backend, and frontend.
3+
Web dashboard for [workmux](https://github.com/raine/workmux). Provides a browser UI with embedded terminals, PR status monitoring, and CI integration on top of workmux's worktree + tmux orchestration.
4+
5+
## What is workmux?
6+
7+
[workmux](https://github.com/raine/workmux) is a CLI tool that orchestrates git worktrees and tmux. It pairs each worktree with a tmux window, provisions files (copy/symlink), runs lifecycle hooks, and has first-class AI agent support. A single `workmux add` creates the worktree, opens a tmux window with configured panes, and starts your agent. `workmux merge` merges the branch, deletes the worktree, closes the window, and cleans up branches.
8+
9+
workmux is configured via `.workmux.yaml` in the project root. See the [workmux README](https://github.com/raine/workmux) for full documentation.
10+
11+
## What wmdev adds
12+
13+
wmdev is a web UI that wraps workmux. It delegates core worktree lifecycle operations to the `workmux` CLI and adds browser-based features on top:
14+
15+
| Responsibility | Handled by |
16+
|---|---|
17+
| Create/remove/merge worktrees | **workmux** (wmdev calls `workmux add`, `workmux rm`, `workmux merge`) |
18+
| Pane layout and agent launch (default profile) | **workmux** (uses `.workmux.yaml` pane config) |
19+
| Open/focus a worktree's tmux window | **workmux** (`workmux open`) |
20+
| List worktrees and agent status | **workmux** (`workmux list`, `workmux status`) |
21+
| File provisioning and lifecycle hooks | **workmux** (`.workmux.yaml` `files` and `post_create`) |
22+
| Browser terminal (xterm.js ↔ tmux) | **wmdev** |
23+
| Service health monitoring (port polling) | **wmdev** |
24+
| PR status tracking and badges | **wmdev** (polls `gh pr list`) |
25+
| CI check status and failed log viewing | **wmdev** (calls `gh run view`) |
26+
| Send prompts / PR comments to agents | **wmdev** (via `tmux load-buffer`) |
27+
| Docker sandbox container lifecycle | **wmdev** (manages `docker run/rm` directly) |
428

529
## Quick start
630

731
```bash
832
# 1. Install prerequisites
9-
cargo install workmux # worktree orchestrator
10-
sudo apt install tmux # (or brew install tmux)
11-
curl -fsSL https://bun.sh/install | bash # bun >1.3.5 required
33+
cargo install workmux # or: brew install raine/workmux/workmux
34+
sudo apt install tmux # or: brew install tmux
35+
curl -fsSL https://bun.sh/install | bash
1236

13-
# 2. Install wmdev globally (from repo root)
37+
# 2. Clone and build
38+
git clone https://github.com/centdix/wmdev.git
39+
cd wmdev
1440
bun install && bun run build && bun link
1541

16-
# 3. Create a .wmdev.yaml in your project root (see Configuration below)
42+
# 3. Set up your project
43+
cd /path/to/your/project
44+
workmux init # creates .workmux.yaml with sensible defaults
45+
46+
# 4. (Optional) Create a .wmdev.yaml for dashboard-specific config
47+
# See Configuration below
1748

18-
# 4. Start the dashboard from your project directory
49+
# 5. Start the dashboard
1950
wmdev # UI on http://localhost:5111
20-
wmdev --port 8080 # or pick a custom port
51+
wmdev --port 8080 # custom port
2152
```
2253

2354
## Configuration
2455

25-
wmdev reads a `.wmdev.yaml` file from the project root. This single file controls services, profiles, and Docker sandbox settings.
56+
wmdev uses two config files in the project root:
2657

27-
### Full schema
58+
- **`.workmux.yaml`** — workmux's own config. Controls worktree directory, pane layout, agent selection, file provisioning, lifecycle hooks, merge strategy, and more. See the [workmux docs](https://github.com/raine/workmux).
59+
- **`.wmdev.yaml`** — dashboard-specific config. Controls service health checks, worktree profiles, linked repos for PR monitoring, and Docker sandbox settings.
60+
61+
### `.wmdev.yaml` schema
2862

2963
```yaml
3064
# Services to monitor — each maps a display name to a port env var.
31-
# The dashboard polls these ports to show health status.
65+
# The dashboard polls these ports and shows health status badges.
3266
services:
3367
- name: string # Display name (e.g. "BE", "FE")
34-
portEnv: string # Env var holding the port number (e.g. "BACKEND_PORT")
68+
portEnv: string # Env var holding the port (e.g. "BACKEND_PORT")
3569

36-
# Profiles define what runs inside a worktree.
70+
# Profiles define the environment when creating a worktree via the dashboard.
3771
profiles:
3872
default: # Required — used when no profile is specified
39-
name: string # Profile identifier (e.g. "full")
40-
systemPrompt: string # (optional) Instructions sent to the AI agent.
41-
# Supports ${VAR} placeholders expanded from env.
73+
name: string # Profile identifier
74+
systemPrompt: string # (optional) Instructions for the AI agent.
75+
# Supports ${VAR} placeholders expanded from .env.local.
4276
envPassthrough: string[] # (optional) Env vars to pass to the agent process
4377

4478
sandbox: # (optional) Docker-based sandboxed profile
45-
name: string # Profile identifier (e.g. "sandbox")
79+
name: string # Profile identifier
4680
image: string # Docker image name (must be pre-built)
4781
systemPrompt: string # (optional) Agent instructions (supports ${VAR})
4882
envPassthrough: string[] # (optional) Host env vars forwarded into the container
4983
extraMounts: # (optional) Additional bind mounts
5084
- hostPath: string # Host path (supports ~ for $HOME)
5185
guestPath: string # (optional) Mount point inside container (defaults to hostPath)
5286
writable: boolean # (optional) true = read-write, false/omit = read-only
87+
88+
# Monitor PRs from other GitHub repos and show their status alongside
89+
# worktree branches that share the same branch name.
90+
linkedRepos:
91+
- repo: string # GitHub repo slug (e.g. "org/repo")
92+
alias: string # (optional) Short label shown in the UI
5393
```
5494
5595
### Defaults
5696
57-
If `.wmdev.yaml` is missing or empty, wmdev uses:
97+
If `.wmdev.yaml` is missing or empty:
5898

5999
```yaml
60100
services: []
61101
profiles:
62102
default:
63103
name: default
104+
linkedRepos: []
64105
```
65106

66107
### Example
67108

68109
```yaml
69110
services:
70111
- name: BE
71-
portEnv: BACKEND_PORT
112+
portEnv: DASHBOARD_PORT
72113
- name: FE
73114
portEnv: FRONTEND_PORT
74115
75116
profiles:
76117
default:
77-
name: full
118+
name: default
78119
79120
sandbox:
80121
name: sandbox
@@ -86,29 +127,37 @@ profiles:
86127
- hostPath: ~/.codex
87128
guestPath: /root/.codex
88129
writable: true
89-
- hostPath: ~/my-private-repo
90-
writable: true
91130
systemPrompt: >
92131
You are running inside a sandboxed container.
93-
Backend port: ${BACKEND_PORT}. Frontend port: ${FRONTEND_PORT}.
132+
Backend port: ${DASHBOARD_PORT}. Frontend port: ${FRONTEND_PORT}.
133+
134+
linkedRepos:
135+
- repo: myorg/related-service
136+
alias: svc
94137
```
95138

96139
### Parameter reference
97140

98141
| Parameter | Type | Required | Description |
99142
|-----------|------|----------|-------------|
100-
| `services[].name` | string | yes | Display name shown in the dashboard UI |
101-
| `services[].portEnv` | string | yes | Name of the env var containing the service port (read from each worktree's `.env.local`) |
143+
| `services[].name` | string | yes | Display name shown in the dashboard |
144+
| `services[].portEnv` | string | yes | Env var containing the service port (read from each worktree's `.env.local`) |
102145
| `profiles.default.name` | string | yes | Identifier for the default profile |
103-
| `profiles.default.systemPrompt` | string | no | System prompt for the agent; `${VAR}` placeholders are expanded at runtime |
146+
| `profiles.default.systemPrompt` | string | no | System prompt for the agent; `${VAR}` placeholders expanded at runtime |
104147
| `profiles.default.envPassthrough` | string[] | no | Env vars passed through to the agent process |
105-
| `profiles.sandbox.name` | string | yes (if sandbox profile used) | Identifier for the sandbox profile |
106-
| `profiles.sandbox.image` | string | yes (if sandbox profile used) | Docker image for containers |
107-
| `profiles.sandbox.systemPrompt` | string | no | System prompt for sandbox agents; `${VAR}` placeholders are expanded at runtime |
148+
| `profiles.sandbox.name` | string | yes (if used) | Identifier for the sandbox profile |
149+
| `profiles.sandbox.image` | string | yes (if used) | Docker image for containers |
150+
| `profiles.sandbox.systemPrompt` | string | no | System prompt for sandbox agents; `${VAR}` placeholders expanded at runtime |
108151
| `profiles.sandbox.envPassthrough` | string[] | no | Host env vars forwarded into the Docker container |
109-
| `profiles.sandbox.extraMounts[].hostPath` | string | yes | Host filesystem path to mount (`~` expands to `$HOME`) |
152+
| `profiles.sandbox.extraMounts[].hostPath` | string | yes | Host path to mount (`~` expands to `$HOME`) |
110153
| `profiles.sandbox.extraMounts[].guestPath` | string | no | Container mount path (defaults to `hostPath`) |
111154
| `profiles.sandbox.extraMounts[].writable` | boolean | no | `true` for read-write; omit or `false` for read-only |
155+
| `linkedRepos[].repo` | string | yes | GitHub repo slug (e.g. `org/repo`) |
156+
| `linkedRepos[].alias` | string | no | Short label for the UI (defaults to repo name) |
157+
158+
### Auto-generated branch names
159+
160+
If your `.workmux.yaml` has `auto_name.model` configured, the create-worktree dialog will automatically generate a branch name from the prompt using that LLM. This is a workmux feature — wmdev detects it and enables the UI flow accordingly.
112161

113162
## Architecture
114163

@@ -127,57 +176,48 @@ Browser (localhost:5111)
127176
lifecycle) access) containers)
128177
```
129178
130-
**Backend** — Bun/TypeScript HTTP + WebSocket server (`backend/src/server.ts`). Two interfaces:
179+
**Backend** — Bun/TypeScript HTTP + WebSocket server (`backend/src/server.ts`):
131180
132-
- **REST API** (`/api/*`) — CRUD for worktrees. Wraps the `workmux` CLI to create/remove/merge worktrees. For sandbox profiles, manages Docker containers directly with published ports. The `GET /api/worktrees` endpoint enriches each worktree with its directory, assigned ports, and service health status.
181+
- **REST API** (`/api/*`) — CRUD for worktrees. Wraps the `workmux` CLI to create/remove/merge worktrees. Enriches each worktree with directory, assigned ports, service health, PR status, and agent state.
133182
- **WebSocket** (`/ws/*`) — Bidirectional terminal bridge between xterm.js in the browser and tmux sessions on the server.
134183
135-
**Frontend** — Svelte 5 SPA with Tailwind CSS and xterm.js (`frontend/src/`). Two-panel UI: worktree list sidebar + embedded terminal. Polls the REST API for status updates. Responsive layout with mobile pane navigation.
184+
**Frontend** — Svelte 5 SPA with Tailwind CSS and xterm.js (`frontend/src/`). Two-panel layout: worktree sidebar + embedded terminal. Polls the REST API for status updates. Responsive with mobile pane navigation.
136185
137186
### Terminal streaming
138187
139-
The WebSocket provides a bidirectional bridge between xterm.js in the browser and a tmux session on the server:
140-
141188
```
142189
Browser (xterm.js) ←— WebSocket —→ Backend ←— stdin/stdout pipes —→ script (PTY) ←— tmux attach —→ tmux grouped session
143190
```
144191
145-
When a worktree is selected, the frontend opens a WebSocket to `/ws/<worktree>` and sends an initial `resize` message with the terminal dimensions. The backend then:
192+
When a worktree is selected, the frontend opens a WebSocket to `/ws/<worktree>`. The backend spawns a PTY via `script` and attaches to a **grouped tmux session** — a separate view into the same windows. This allows the dashboard and a real terminal to view the same worktree simultaneously.
146193
147-
1. Spawns `script -q -c "... tmux attach-session ..." /dev/null` — allocates a real PTY for proper terminal escape sequences.
148-
2. Creates a **grouped tmux session**, which is a separate view into the same windows. This allows the dashboard and a real terminal to view the same worktree simultaneously.
149-
3. Streams PTY stdout over the WebSocket as `{ type: "output" }` messages.
150-
4. Writes keystrokes from `{ type: "input" }` messages to the PTY's stdin.
151-
5. Handles `resize` events by calling `tmux resize-window`.
152-
153-
Output is buffered in a scrollback array (up to 5000 chunks) so reconnecting clients receive recent history immediately.
194+
Output is buffered (up to 1 MB) so reconnecting clients receive recent history immediately.
154195
155196
### Worktree profiles
156197
157-
When creating a worktree, you pick a profile that determines the environment:
158-
159198
| Profile | What it does |
160199
|---------|-------------|
161-
| `default` | Delegates to workmux — uses the pane layout and commands defined in your `.workmux.yaml` project config. wmdev doesn't manage panes or processes for this profile; workmux handles it all. |
162-
| `sandbox` | Managed entirely by wmdev, decoupled from workmux. wmdev launches a Docker container, sets up agent + shell panes, and publishes service ports directly with `docker run -p`. |
200+
| `default` | Delegates to workmux — uses the pane layout and commands from `.workmux.yaml`. wmdev doesn't manage panes or processes; workmux handles it all. |
201+
| `sandbox` | Managed by wmdev. Launches a Docker container, sets up agent + shell panes, and publishes service ports via `docker run -p`. |
163202
164203
### Docker sandbox containers
165204
166205
For sandbox profiles, wmdev manages Docker containers directly:
167206
168-
1. **Launch** — `docker run -d -p <ports>` with the configured image, mounts, and env vars
169-
2. **Mounts** — Worktree dir (rw), main repo `.git` (ro), Claude config, plus any `extraMounts`
170-
3. **Environment** — All `.env.local` vars + `envPassthrough` vars + `HOME`, `TERM`, `IS_SANDBOX=1`
171-
4. **Cleanup** — Containers are removed when the worktree is removed or merged
207+
1. **Launch** — `docker run -d -p <ports>` with the configured image, mounts, and env vars. Runs as the host user (`--user uid:gid`) so file ownership matches.
208+
2. **Mounts** — Worktree dir (rw), main repo `.git` (rw), main repo root (ro), `~/.claude`, plus any `extraMounts`. Conditionally mounts `~/.gitconfig`, `~/.ssh`, `~/.config/gh` if they exist.
209+
3. **Environment** — All `.env.local` vars + `envPassthrough` vars + `HOME`, `TERM`, `IS_SANDBOX=1`.
210+
4. **Cleanup** — Containers are removed when the worktree is removed or merged.
172211
173212
## Prerequisites
174213
175214
| Tool | Min version | Purpose |
176215
|------|-------------|---------|
177-
| [**bun**](https://bun.sh) | >1.3.5 | Runtime for backend and frontend |
178-
| [**workmux**](https://github.com/raine/workmux) | latest | Worktree + tmux orchestration (`cargo install workmux`) |
216+
| [**bun**](https://bun.sh) | 1.3.5+ | Runtime for backend and frontend |
217+
| [**workmux**](https://github.com/raine/workmux) | latest | Worktree + tmux orchestration |
179218
| **tmux** | 3.x | Terminal multiplexer |
180219
| **git** | 2.x | Worktree management |
220+
| **gh** | 2.x | PR and CI status (optional — needed for PR badges and CI logs) |
181221
| **docker** | 28+ | Only needed for sandbox profile |
182222
183223
## Environment variables
@@ -200,12 +240,14 @@ For sandbox profiles, wmdev manages Docker containers directly:
200240
| Method | Endpoint | Description |
201241
|--------|----------|-------------|
202242
| `GET` | `/api/config` | Load dashboard config |
203-
| `GET` | `/api/worktrees` | List all worktrees with status, ports, and service health |
204-
| `POST` | `/api/worktrees` | Create a worktree (`{ branch, profile?, agent?, prompt? }`) |
243+
| `GET` | `/api/worktrees` | List all worktrees with status, ports, service health, and PR data |
244+
| `POST` | `/api/worktrees` | Create a worktree (`{ branch?, profile?, agent?, prompt? }`) |
205245
| `DELETE` | `/api/worktrees/:name` | Remove a worktree |
206246
| `POST` | `/api/worktrees/:name/open` | Open/focus a worktree's tmux window |
207247
| `POST` | `/api/worktrees/:name/merge` | Merge worktree into main + cleanup |
208248
| `GET` | `/api/worktrees/:name/status` | Get agent status for a worktree |
249+
| `POST` | `/api/worktrees/:name/send` | Send a prompt to the agent's tmux pane |
250+
| `GET` | `/api/ci-logs/:runId` | Fetch failed CI run logs |
209251
| `WS` | `/ws/:worktree` | Terminal WebSocket (xterm.js ↔ tmux) |
210252
211253
## Development
@@ -224,4 +266,4 @@ cd backend && bun run dev
224266
cd frontend && bun run dev
225267
```
226268

227-
The frontend dev server runs on port `5112` and proxies `/api/*` and `/ws/*` to the backend.
269+
The frontend dev server runs on port `5112` and proxies `/api/*` and `/ws/*` to the backend on `5111`.

0 commit comments

Comments
 (0)