# Windmill Development with workmux This guide covers the workmux-based development setup for Windmill. Each worktree gets its own tmux window with a Claude Code agent, a backend server (with auto-reload), and a frontend dev server — all on isolated ports. ## Prerequisites - tmux - Rust toolchain (rustup) - Node.js + npm - PostgreSQL running locally (see `backend/.env`) ## Installation ### 1. Install workmux ```bash cargo install workmux ``` ### 2. Install the Claude Code plugin ```bash workmux claude install ``` This lets workmux manage Claude Code agents in worktree panes. ### 3. Install cargo-watch Used for auto-recompiling the backend on file changes: ```bash cargo install cargo-watch ``` ### 4. Install llm CLI (required for auto branch naming) workmux uses the `llm` CLI to automatically generate branch names from prompts. Install it with: ```bash uv tool install llm llm install llm-anthropic ``` Then set your Anthropic API key: ```bash llm keys set anthropic # paste your API key when prompted ``` ### 5. Recommended: shell alias and autocomplete Set up a `wm` alias for convenience: ```bash # Add to your ~/.zshrc alias wm="workmux" ``` Setting up zsh autocomplete is also recommended — see the [workmux docs](https://github.com/rubenfiszel/workmux) for instructions. ## Port Slot System Each worktree is assigned a **slot** that determines its ports: | Slot | Backend | Frontend | | ---- | ------- | -------- | | 0 | 8000 | 3000 | | 1 | 8010 | 3010 | | 2 | 8020 | 3020 | | 3 | 8030 | 3030 | | ... | ... | ... | - **Slot 0** is reserved for the main worktree (default `cargo run` / `npm run dev`). - Without `WM_SLOT`, the script auto-assigns the first available slot (starting from 1) and prints it. - With `WM_SLOT=N`, it uses that slot and errors if the ports are taken. ## SSH Port Forwarding If you develop over SSH, add this to `~/.ssh/config` on your **local machine** to pre-configure tunnels for each slot: ``` Host windmill-dev HostName User # Slot 0 (main worktree) LocalForward 8000 localhost:8000 LocalForward 3000 localhost:3000 # Slot 1 LocalForward 8010 localhost:8010 LocalForward 3010 localhost:3010 # Slot 2 LocalForward 8020 localhost:8020 LocalForward 3020 localhost:3020 # Slot 3 LocalForward 8030 localhost:8030 LocalForward 3030 localhost:3030 ``` Then connect once and all tunnels are active: ```bash ssh windmill-dev ``` Access the frontend at `http://localhost:` in your local browser. ## Quickstart ```bash # Create a new worktree (auto-assigns slot, prints ports) workmux add my-feature # Or with an explicit slot WM_SLOT=2 workmux add my-feature # Create a worktree and immediately send a prompt to the agent workmux add -A -p "fix the login bug in auth.rs" ``` The `add` command creates the worktree but does **not** open it. To open the tmux window and start working: ```bash workmux open my-feature ``` This will open a tmux window with three panes: - **Claude Code agent** (focused) - **Backend**: `cargo watch -x run` on the assigned port (auto-reloads on save) - **Frontend**: `npm run dev` proxying to the backend When using `-A` with `add`, the worktree is created and opened automatically, and the prompt is sent to the agent right away. Check which ports were assigned: ```bash cat /.env.local ``` ### Sending work to the agent ```bash # Send a prompt to the agent in a worktree workmux send my-feature "fix the login bug in auth.rs" # Check agent status workmux status ``` ### Merging and cleaning up We never merge worktrees directly — always create a PR on GitHub and let it be merged there. Once the PR is merged, clean up the worktree: ```bash # Close the tmux window but keep the worktree workmux close my-feature # After your PR is merged, remove the worktree, branch, and tmux window workmux rm my-feature ``` > **Note**: Do not use `workmux merge`. Always go through a PR to get your changes into main. You can ask the Claude Code agent in the worktree to create the PR for you. ## Configuration The setup is defined in `.workmux.yaml` at the repo root. Key sections: - **`post_create`**: Runs `scripts/worktree-env` to generate `.env.local` with port assignments - **`panes`**: Defines the tmux layout (agent, backend, frontend) - **`files.copy`**: Copies `backend/.env` and `scripts/` into each worktree The `post_create` hook also copies `frontend/node_modules` using `cp -a` (preserves `.bin/` symlinks that `cp -r` would dereference). ## Enterprise (EE) Code Access The enterprise source code lives in the `windmill-ee-private` repository (sibling to this repo). When you create a worktree, `scripts/worktree-env` automatically creates a matching EE worktree on the same branch and configures Claude Code's `additionalDirectories` to grant access. ### Sandbox setup When using sandbox mode, the container needs explicit mounts to access the EE repo. Add the following to your global workmux config (`~/.config/workmux/config.yaml`): ```yaml sandbox: extra_mounts: - host_path: ~/windmill-ee-private writable: true - host_path: ~/windmill-ee-private__worktrees writable: true ``` This mounts both the main EE repo (used by the main worktree) and the EE worktrees directory (used by feature worktrees) into every sandbox container. ## Cargo Features To build the backend with specific Cargo features (e.g., `enterprise`, `parquet`), pass them via `CARGO_FEATURES`. The backend pane reads this from `.env.local` and appends `--features ` to the `cargo watch` command. **With `wm` (workmux):** Set `CARGO_FEATURES` as an environment variable before creating the worktree: ```bash CARGO_FEATURES="enterprise,parquet" wm add my-feature ``` This gets written to `.env.local` by the `post_create` hook (`scripts/worktree-env`), and the backend pane picks it up automatically. ## Login Default credentials: `admin@windmill.dev` / `changeme`