Files
windmill/README_WORKMUX_DEV.md
hugocasa 96229575e6 chore: dev tooling — wm-ts-nav navigator, format hooks, review skill (#8337)
* chore: remove wm-cursor, add local-review skill, update PR skill for EE

- Remove the unused wm-cursor script and all references to it in
  README_WORKMUX_DEV.md and worktree-common.sh
- Add /local-review skill for code review (bugs + CLAUDE.md compliance)
- Add EE companion PR workflow to the /pr skill

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add wm-ts-nav tree-sitter navigator and fix format hooks

- Add wm-ts-nav: standalone tree-sitter code navigator with SQLite index
  for fast symbol search, definition lookup, and file outlines across
  Rust, TypeScript, and Svelte files (~12ms warm, ~1s cold for 482 files)
- Fix format hooks: surface errors instead of swallowing with 2>/dev/null,
  use direct prettier path with svelte plugin, add success feedback
- Add wm-ts-nav commands to settings allow list
- Document wm-ts-nav usage in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): add refs command and --parent filter

- refs: find usages of a symbol in code, skipping comments and strings
  (tree-sitter AST walk, ~46ms for 482 files vs grep's 4ms but no noise)
- --parent filter on search: find all methods on a type across all files
  (e.g. search "%" --kind function --parent ServiceName)
- Update CLAUDE.md with clearer when-to-use guidance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): index refs in DB with import-path resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(wm-ts-nav): add body, callers, callees commands and refs --file/--caller

- body: extract a symbol's source code from disk using indexed line ranges
- callers: cross-file call graph via SQL join of refs + symbols tables
- callees: list all identifiers referenced within a symbol's body
- refs --file: scope results to files matching a substring
- refs --caller: annotate each ref with the containing function name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(wm-ts-nav): add auto-rebuilding wrapper script

The `wm-ts-nav/nav` wrapper checks if source files are newer than the
binary and rebuilds automatically. Invoked via `sh wm-ts-nav/nav` to
avoid needing executable permissions after clone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: tighten CLAUDE.md nav section for actionable guidance

Remove redundant question→command mapping, latency numbers, and
excessive examples. Lead with "prefer wm-ts-nav over Read to save
context window" and keep only the patterns that change behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: revert backend/Cargo.lock to main

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: promote wm-ts-nav in workflow, copy binary to worktrees

- CLAUDE.md: integrate wm-ts-nav into Workflow step 1 and Core
  Principles so agents use outline/body before full file reads
- workmux: copy built binary via files.copy
- worktree-common.sh: copy binary in wm_copy_dependencies for webmux

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wm-ts-nav): fix double indexing, add TSX grammar, remove needless clone

- Reuse index stats from the pre-query update instead of indexing twice
  on the Index command
- Add Lang::Tsx variant so .tsx/.jsx files use LANGUAGE_TSX instead of
  LANGUAGE_TYPESCRIPT (Svelte stays on TS since script blocks are pure TS)
- Remove source.clone() for non-Svelte files — move directly instead

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wm-ts-nav): fix svelte line numbers, add class methods, innermost caller

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:07:49 +00:00

5.9 KiB

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

cargo install workmux

2. Install the Claude Code plugin

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:

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:

uv tool install llm
llm install llm-anthropic

Then set your Anthropic API key:

llm keys set anthropic
# paste your API key when prompted

Set up a wm alias for convenience:

# Add to your ~/.zshrc
alias wm="workmux"

Setting up zsh autocomplete is also recommended — see the workmux docs 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 <remote-ip>
  User <username>
  # 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:

ssh windmill-dev

Access the frontend at http://localhost:<frontend-port> in your local browser.

Quickstart

# 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:

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:

cat <worktree-path>/.env.local

Sending work to the agent

# 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:

# 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):

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 <value> to the cargo watch command.

With wm (workmux):

Set CARGO_FEATURES as an environment variable before creating the worktree:

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