Labsco
vercel logo

internal-dev-workbench

✓ Official2,200

by vercel · part of vercel/workflow

Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `<branch>.<name>.localhost` URLs for the Next.js workbench and…

🔥✓ VerifiedFreeQuick setup
🔒 Repo-maintenance skill. It exists to help maintain vercel/workflow itself — it's only useful if you contribute code to that project.

Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `<branch>.<name>.localhost` URLs for the Next.js workbench and…

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.

by vercel

Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated <branch>.<name>.localhost URLs for the Next.js workbench and… npx skills add https://github.com/vercel/workflow --skill internal-dev-workbench Download ZIPGitHub2.2k

internal-dev-workbench

Bootstraps an opinionated 3-pane tmux session for end-to-end Workflow SDK development. Each pane is launched through portless so URLs are stable and worktree-scoped (e.g. https://<branch>.turbopack.localhost), letting multiple worktrees run concurrently without port conflicts. A companion statusline script surfaces the active URLs in Claude Code's prompt.

This is opt-in contributor tooling. The repo's standard dev path (pnpm dev from a workbench, no portless) is unaffected.

Layout

main-vertical — the dev server takes the left column; the right column stacks the observability UI on top of a scratchpad shell:

Copy & paste — that's it
+----------------------+--------------------------+
| | PANE_OBS: workflow web |
| | (observability UI |
| PANE_DEV: turbopack | scoped to the |
| (Next.js dev) | workbench app) |
| +--------------------------+
| | PANE_SH: zsh scratchpad |
| | (repo root — for build, |
| | tests, e2e, git, etc.) |
+----------------------+--------------------------+

Why each piece

  • portless run --name <name> (instead of portless <name> <cmd>): run auto-detects git worktrees and prepends the sanitized branch name as a subdomain. The --name flag overrides the inferred base name while preserving the worktree prefix.

  • pnpm workflow web --webPort $PORT --noBrowser (instead of pnpm dev in packages/web): the bundled CLI starts the observability UI configured against the current workbench app, hydrating it with that project's local World data. Running packages/web's own dev script gives you the UI but pointed at nothing.

  • sh -c '... --webPort $PORT': portless's auto --port injection only triggers for known frameworks it can detect on the command line. When the command is a CLI wrapper (pnpm workflow web), wrap in sh -c and read $PORT (which portless always sets) explicitly.

  • WORKFLOW_PUBLIC_MANIFEST=1 on the dev pane: required for e2e tests to fetch the workflow registry from the dev server.

  • -P -F '#{pane_id}': makes the snippet correct regardless of the user's pane-base-index setting (defaults vary across configs).

Claude statusline integration

The skill ships a statusline helper at skills/internal-dev-workbench/statusline.sh that derives the worktree prefix from the current branch and emits a compact line:

Copy & paste — that's it
 dev · obs · tmux attach -t 

The dev / obs labels (Nerd Font rocket / graph glyphs) are OSC 8 hyperlinks — clickable in iTerm2, Kitty, WezTerm, Terminal.app, Ghostty — styled bold + underlined + bright cyan so they read unambiguously as links. The tmux fragment (Nerd Font copy glyph) is bold bright green, signaling "copy this" rather than "click this". It's shown only when a session named exactly the worktree prefix exists, and it's printed as a full ready-to-paste tmux attach -t <prefix> invocation. The font must include Nerd Font glyphs for the icons to render correctly; without them you'll see substitution boxes but the layout still works. Each piece is independent — if portless has no <prefix>.turbopack.localhost route, the dev fragment is omitted, and so on. With nothing to show, the script prints nothing and the statusline stays silent.

Wire it into ~/.claude/settings.json so it works across all sessions and worktrees. Point the path at your primary checkout, not at a worktree — worktrees get deleted, so any path like ~/github/vercel/workflow--<branch>/... will break the day you remove that worktree:

Copy & paste — that's it
{
 "statusLine": {
 "type": "command",
 "command": "$HOME/github/vercel/workflow/skills/internal-dev-workbench/statusline.sh"
 }
}

Adjust the prefix if your main checkout lives elsewhere. The script itself is worktree-aware: it reads Claude's workspace.current_dir from stdin to derive the current branch, so the same script invocation from ~/github/vercel/workflow/... correctly surfaces routes for whichever worktree the Claude session is running in.

Output rules:

  • Nothing to show (no matching portless route, no matching tmux session) → empty output.

  • Each piece appears independently — start a server but no tmux session and you'll see just the dev/obs fragments; the reverse shows just the tmux fragment.

  • No git context but routes exist → falls back to the first matching turbopack/workflow-obs route, no tmux indicator.

If you already use a statusline and want to append the internal-dev-workbench info, run the helper and concatenate in your existing wrapper script instead of replacing command outright.

Restarting after editing workflow files

The workflow manifest is built at dev-server startup. New workflows or steps added to workbench/example/workflows/*.ts (and their symlinks in other workbenches) do not appear at runtime — even with HMR — until the dev server restarts.

Copy & paste — that's it
tmux send-keys -t "$PANE_DEV" C-c
# Wait for the prompt to return
tmux send-keys -t "$PANE_DEV" \
 'cd workbench/nextjs-turbopack && WORKFLOW_PUBLIC_MANIFEST=1 portless run --name turbopack pnpm dev' C-m

Verify the new workflow is registered (use the portless-assigned local port from portless list, or the .localhost URL with the trusted CA):

Copy & paste — that's it
/usr/bin/curl -s "$(portless get turbopack)/.well-known/workflow/v1/manifest.json" \
 | grep -o ' '

NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem is needed for Node clients hitting the HTTPS URL outside of portless-managed children. Browsers are fine after portless trust.

Teardown

Copy & paste — that's it
tmux kill-session -t "$SESSION"

Portless removes routes when each child process exits (Ctrl+C the panes first if you want a clean portless list). The proxy itself keeps running for other sessions; stop it explicitly with portless proxy stop if needed.