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. ), 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 ( from a workbench, no portless) is unaffected. Prerequisites - installed - installed globally ( or via Homebrew). Verify with . - Repo bootstrapped: . The first run on a fresh worktree mu…

'\nICON_OBS=

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. ), 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 ( from a workbench, no portless) is unaffected. Prerequisites - installed - installed globally ( or via Homebrew). Verify with . - Repo bootstrapped: . The first run on a fresh worktree mu…

'\nICON_CP=

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. ), 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 ( from a workbench, no portless) is unaffected. Prerequisites - installed - installed globally ( or via Homebrew). Verify with . - Repo bootstrapped: . The first run on a fresh worktree mu…

'\n\nif [ -n \"$dev_url\" ]; then\n sep\n emit_link \"$dev_url\" \"${ICON_DEV} dev\"\nfi\nif [ -n \"$obs_url\" ]; then\n sep\n emit_link \"$obs_url\" \"${ICON_OBS} obs\"\nfi\nif [ -n \"$session\" ]; then\n sep\n emit_tmux \"$session\"\nfi\n\nprintf '\\n'\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4182,"content_sha256":"94a9cf861d856de60a73b597e722878cd7836481e856783e7d1c205318ccf6b9"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"internal-dev-workbench","type":"text"}]},{"type":"paragraph","content":[{"text":"Bootstraps an opinionated 3-pane tmux session for end-to-end Workflow SDK development. Each pane is launched through ","type":"text"},{"text":"portless","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/aleclarson/portless","title":null}}]},{"text":" so URLs are stable and worktree-scoped (e.g. ","type":"text"},{"text":"https://\u003cbranch>.turbopack.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":"), letting multiple worktrees run concurrently without port conflicts. A companion statusline script surfaces the active URLs in Claude Code's prompt.","type":"text"}]},{"type":"paragraph","content":[{"text":"This is ","type":"text"},{"text":"opt-in contributor tooling","type":"text","marks":[{"type":"strong"}]},{"text":". The repo's standard dev path (","type":"text"},{"text":"pnpm dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" from a workbench, no portless) is unaffected.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"tmux","type":"text","marks":[{"type":"code_inline"}]},{"text":" installed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"portless","type":"text","marks":[{"type":"code_inline"}]},{"text":" installed globally (","type":"text"},{"text":"npm i -g portless","type":"text","marks":[{"type":"code_inline"}]},{"text":" or via Homebrew). Verify with ","type":"text"},{"text":"portless --version","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Repo bootstrapped: ","type":"text"},{"text":"pnpm install && pnpm build","type":"text","marks":[{"type":"code_inline"}]},{"text":". The first run on a fresh worktree must complete both before any dev server can start (the workbench apps depend on built workspace packages — without ","type":"text"},{"text":"pnpm build","type":"text","marks":[{"type":"code_inline"}]},{"text":" you get ","type":"text"},{"text":"MODULE_NOT_FOUND","type":"text","marks":[{"type":"code_inline"}]},{"text":" for ","type":"text"},{"text":"workflow","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WORKFLOW_PUBLIC_MANIFEST=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" is required on the dev server when running e2e tests against it (otherwise ","type":"text"},{"text":"/.well-known/workflow/v1/manifest.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" is gated).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Layout","type":"text"}]},{"type":"paragraph","content":[{"text":"main-vertical","type":"text","marks":[{"type":"code_inline"}]},{"text":" — the dev server takes the left column; the right column stacks the observability UI on top of a scratchpad shell:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"+----------------------+--------------------------+\n| | PANE_OBS: workflow web |\n| | (observability UI |\n| PANE_DEV: turbopack | scoped to the |\n| (Next.js dev) | workbench app) |\n| +--------------------------+\n| | PANE_SH: zsh scratchpad |\n| | (repo root — for build, |\n| | tests, e2e, git, etc.) |\n+----------------------+--------------------------+","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"The session name ","type":"text"},{"text":"must","type":"text","marks":[{"type":"strong"}]},{"text":" match the worktree's portless prefix — the basename of the current branch — so the statusline (and any other tooling that derives the prefix from the branch) can locate it. Always run ","type":"text"},{"text":"tmux ls","type":"text","marks":[{"type":"code_inline"}]},{"text":" first to confirm there's no pre-existing session with that name; never kill an existing one.","type":"text"}]},{"type":"paragraph","content":[{"text":"Pane indices in tmux depend on ","type":"text"},{"text":"pane-base-index","type":"text","marks":[{"type":"code_inline"}]},{"text":" (0 by default, 1 with the common dotfile override). To stay correct under either, capture each pane's ID at split time with ","type":"text"},{"text":"-P -F '#{pane_id}'","type":"text","marks":[{"type":"code_inline"}]},{"text":" and use those IDs as targets:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"REPO=/path/to/workflow--\u003cworktree-suffix>\n# Session name = basename of the branch (matches portless's subdomain prefix\n# and the statusline's `tmux attach -t \u003cprefix>` indicator). For branch\n# `pgp/foo-bar` this resolves to `foo-bar`.\nSESSION=$(git -C \"$REPO\" rev-parse --abbrev-ref HEAD)\nSESSION=\"${SESSION##*/}\"\n\n# Create the session and capture the initial pane ID\nPANE_DEV=$(tmux new-session -d -s \"$SESSION\" -c \"$REPO\" -P -F '#{pane_id}')\nPANE_OBS=$(tmux split-window -h -t \"$PANE_DEV\" -c \"$REPO\" -P -F '#{pane_id}')\nPANE_SH=$(tmux split-window -v -t \"$PANE_OBS\" -c \"$REPO\" -P -F '#{pane_id}')\ntmux select-layout -t \"$SESSION\" main-vertical\n\n# Pane DEV (left): Next.js turbopack workbench, with manifest exposed for e2e\ntmux send-keys -t \"$PANE_DEV\" \\\n 'cd workbench/nextjs-turbopack && WORKFLOW_PUBLIC_MANIFEST=1 portless run --name turbopack pnpm dev' C-m\n\n# Pane OBS (top-right): observability UI scoped to the workbench app\ntmux send-keys -t \"$PANE_OBS\" \\\n 'cd workbench/nextjs-turbopack && portless run --name workflow-obs sh -c \"pnpm workflow web --webPort \\$PORT --noBrowser\"' C-m\n\n# Pane SH (bottom-right): scratchpad at repo root\ntmux send-keys -t \"$PANE_SH\" 'echo \"scratchpad: $(pwd)\"' C-m\n\ntmux attach -t \"$SESSION\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Once both servers are ready, ","type":"text"},{"text":"portless list","type":"text","marks":[{"type":"code_inline"}]},{"text":" shows the routes. With ","type":"text"},{"text":"portless run","type":"text","marks":[{"type":"code_inline"}]},{"text":", each linked worktree gets a unique branch-prefixed subdomain (e.g. ","type":"text"},{"text":"stepflow-test.turbopack.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":"), so multiple worktrees coexist without changing config.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Why each piece","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"portless run --name \u003cname>","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (instead of ","type":"text"},{"text":"portless \u003cname> \u003ccmd>","type":"text","marks":[{"type":"code_inline"}]},{"text":"): ","type":"text"},{"text":"run","type":"text","marks":[{"type":"code_inline"}]},{"text":" auto-detects git worktrees and prepends the sanitized branch name as a subdomain. The ","type":"text"},{"text":"--name","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag overrides the inferred base name while preserving the worktree prefix.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pnpm workflow web --webPort $PORT --noBrowser","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (instead of ","type":"text"},{"text":"pnpm dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"packages/web","type":"text","marks":[{"type":"code_inline"}]},{"text":"): the bundled CLI starts the observability UI configured against the ","type":"text"},{"text":"current workbench app","type":"text","marks":[{"type":"strong"}]},{"text":", hydrating it with that project's local World data. Running ","type":"text"},{"text":"packages/web","type":"text","marks":[{"type":"code_inline"}]},{"text":"'s own ","type":"text"},{"text":"dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" script gives you the UI but pointed at nothing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sh -c '... --webPort $PORT'","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": portless's auto ","type":"text"},{"text":"--port","type":"text","marks":[{"type":"code_inline"}]},{"text":" injection only triggers for known frameworks it can detect on the command line. When the command is a CLI wrapper (","type":"text"},{"text":"pnpm workflow web","type":"text","marks":[{"type":"code_inline"}]},{"text":"), wrap in ","type":"text"},{"text":"sh -c","type":"text","marks":[{"type":"code_inline"}]},{"text":" and read ","type":"text"},{"text":"$PORT","type":"text","marks":[{"type":"code_inline"}]},{"text":" (which portless always sets) explicitly.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WORKFLOW_PUBLIC_MANIFEST=1","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" on the dev pane: required for e2e tests to fetch the workflow registry from the dev server.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-P -F '#{pane_id}'","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": makes the snippet correct regardless of the user's ","type":"text"},{"text":"pane-base-index","type":"text","marks":[{"type":"code_inline"}]},{"text":" setting (defaults vary across configs).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Claude statusline integration","type":"text"}]},{"type":"paragraph","content":[{"text":"The skill ships a statusline helper at ","type":"text"},{"text":"skills/internal-dev-workbench/statusline.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" that derives the worktree prefix from the current branch and emits a compact line:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":" dev · obs · tmux attach -t \u003cworktree-prefix>","type":"text"}]},{"type":"paragraph","content":[{"text":"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 ","type":"text"},{"text":"tmux attach -t \u003cprefix>","type":"text","marks":[{"type":"code_inline"}]},{"text":" 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 ","type":"text"},{"text":"\u003cprefix>.turbopack.localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" route, the dev fragment is omitted, and so on. With nothing to show, the script prints nothing and the statusline stays silent.","type":"text"}]},{"type":"paragraph","content":[{"text":"Wire it into ","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" so it works across all sessions and worktrees. ","type":"text"},{"text":"Point the path at your primary checkout, not at a worktree","type":"text","marks":[{"type":"strong"}]},{"text":" — worktrees get deleted, so any path like ","type":"text"},{"text":"~/github/vercel/workflow--\u003cbranch>/...","type":"text","marks":[{"type":"code_inline"}]},{"text":" will break the day you remove that worktree:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"$HOME/github/vercel/workflow/skills/internal-dev-workbench/statusline.sh\"\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Adjust the prefix if your main checkout lives elsewhere. The script itself is worktree-aware: it reads Claude's ","type":"text"},{"text":"workspace.current_dir","type":"text","marks":[{"type":"code_inline"}]},{"text":" from stdin to derive the current branch, so the ","type":"text"},{"text":"same script invocation","type":"text","marks":[{"type":"em"}]},{"text":" from ","type":"text"},{"text":"~/github/vercel/workflow/...","type":"text","marks":[{"type":"code_inline"}]},{"text":" correctly surfaces routes for whichever worktree the Claude session is running in.","type":"text"}]},{"type":"paragraph","content":[{"text":"Output rules:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Nothing to show (no matching portless route, no matching tmux session) → empty output.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"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.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No git context but routes exist → falls back to the first matching ","type":"text"},{"text":"turbopack","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"workflow-obs","type":"text","marks":[{"type":"code_inline"}]},{"text":" route, no tmux indicator.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"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 ","type":"text"},{"text":"command","type":"text","marks":[{"type":"code_inline"}]},{"text":" outright.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Restarting after editing workflow files","type":"text"}]},{"type":"paragraph","content":[{"text":"The workflow manifest is built at dev-server startup. New workflows or steps added to ","type":"text"},{"text":"workbench/example/workflows/*.ts","type":"text","marks":[{"type":"code_inline"}]},{"text":" (and their symlinks in other workbenches) ","type":"text"},{"text":"do not appear at runtime","type":"text","marks":[{"type":"strong"}]},{"text":" — even with HMR — until the dev server restarts.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"tmux send-keys -t \"$PANE_DEV\" C-c\n# Wait for the prompt to return\ntmux send-keys -t \"$PANE_DEV\" \\\n 'cd workbench/nextjs-turbopack && WORKFLOW_PUBLIC_MANIFEST=1 portless run --name turbopack pnpm dev' C-m","type":"text"}]},{"type":"paragraph","content":[{"text":"Verify the new workflow is registered (use the portless-assigned local port from ","type":"text"},{"text":"portless list","type":"text","marks":[{"type":"code_inline"}]},{"text":", or the ","type":"text"},{"text":".localhost","type":"text","marks":[{"type":"code_inline"}]},{"text":" URL with the trusted CA):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/usr/bin/curl -s \"$(portless get turbopack)/.well-known/workflow/v1/manifest.json\" \\\n | grep -o '\u003cyour-new-workflow>'","type":"text"}]},{"type":"paragraph","content":[{"text":"NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem","type":"text","marks":[{"type":"code_inline"}]},{"text":" is needed for Node clients hitting the HTTPS URL outside of portless-managed children. Browsers are fine after ","type":"text"},{"text":"portless trust","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Running e2e tests against this session","type":"text"}]},{"type":"paragraph","content":[{"text":"From the scratchpad pane. Use the portless-assigned local port to bypass TLS for the test runner:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"PORT=$(portless list | awk '/turbopack/ {n=split($3,a,\":\"); print a[n]; exit}')\nDEPLOYMENT_URL=\"http://localhost:$PORT\" APP_NAME=\"nextjs-turbopack\" \\\n pnpm vitest run packages/core/e2e/e2e.test.ts -t \"\u003ctest name>\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Or use the portless URL with the CA trust:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem \\\n DEPLOYMENT_URL=\"$(portless get turbopack)\" APP_NAME=\"nextjs-turbopack\" \\\n pnpm vitest run packages/core/e2e/e2e.test.ts -t \"\u003ctest name>\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Teardown","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"tmux kill-session -t \"$SESSION\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Portless removes routes when each child process exits (Ctrl+C the panes first if you want a clean ","type":"text"},{"text":"portless list","type":"text","marks":[{"type":"code_inline"}]},{"text":"). The proxy itself keeps running for other sessions; stop it explicitly with ","type":"text"},{"text":"portless proxy stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" if needed.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MODULE_NOT_FOUND: 'workflow'","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" in the dev pane — workspace packages haven't been built. Run ","type":"text"},{"text":"pnpm build","type":"text","marks":[{"type":"code_inline"}]},{"text":" from the repo root, then restart the pane.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Observability UI shows no runs","type":"text","marks":[{"type":"strong"}]},{"text":" — verify the obs pane was started from inside ","type":"text"},{"text":"workbench/nextjs-turbopack","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or whichever workbench you want to inspect). The CLI reads the local World from the ","type":"text"},{"text":"current working directory","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"react-router on ","type":"text","marks":[{"type":"strong"}]},{"text":":5173","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" instead of the portless port","type":"text","marks":[{"type":"strong"}]},{"text":" — happens when the obs pane uses ","type":"text"},{"text":"pnpm dev","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"packages/web","type":"text","marks":[{"type":"code_inline"}]},{"text":". Switch to the ","type":"text"},{"text":"pnpm workflow web --webPort $PORT","type":"text","marks":[{"type":"code_inline"}]},{"text":" form above.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Source-map warning on startup","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"failed to read input source map ... packages/serde/dist/index.js.map","type":"text","marks":[{"type":"code_inline"}]},{"text":") — benign; doesn't block dev.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stale workflow registration","type":"text","marks":[{"type":"strong"}]},{"text":" after editing ","type":"text"},{"text":"99_e2e.ts","type":"text","marks":[{"type":"code_inline"}]},{"text":" — restart the dev pane; HMR doesn't rebuild the manifest.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Statusline shows nothing","type":"text","marks":[{"type":"strong"}]},{"text":" — confirm ","type":"text"},{"text":"portless list","type":"text","marks":[{"type":"code_inline"}]},{"text":" has at least one matching route, the path in ","type":"text"},{"text":"settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" is absolute, and the script is executable (","type":"text"},{"text":"chmod +x","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"internal-dev-workbench","author":"@skillopedia","source":{"stars":2064,"repo_name":"workflow","origin_url":"https://github.com/vercel/workflow/blob/HEAD/skills/internal-dev-workbench/SKILL.md","repo_owner":"vercel","body_sha256":"57d5f08e0aaf4ac72fc4b97d4e292b8a007d2e44a1f8726752e83cf990be983a","cluster_key":"22993d884fefab1dc6881651e9dc71c51852ca11f0da85c9e7d2b5a72d2daa49","clean_bundle":{"format":"clean-skill-bundle-v1","source":"vercel/workflow/skills/internal-dev-workbench/SKILL.md","attachments":[{"id":"57192c8d-4d6d-5a0e-9776-6096334e1db7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/57192c8d-4d6d-5a0e-9776-6096334e1db7/attachment.sh","path":"statusline.sh","size":4182,"sha256":"94a9cf861d856de60a73b597e722878cd7836481e856783e7d1c205318ccf6b9","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"63372696824e9a725440af07c833f0abc0b3e2822470e9bb50bf64f13228562f","attachment_count":1,"text_attachments":1,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/internal-dev-workbench/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"web-development","metadata":{"author":"Pranay Prakash","version":"0.1"},"import_tag":"clean-skills-v1","description":"Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `\u003cbranch>.\u003cname>.localhost` URLs for the Next.js workbench and the observability UI, plus a Claude statusline that surfaces those URLs. Use only when the user asks for a \"portless dev session\", a \"tmux dev layout for workflow\", \"worktree-isolated dev URLs\", or wants to wire workflow dev URLs into the Claude statusline. Do not activate for the generic \"start the dev server\" / \"run pnpm dev\" task."}},"renderedAt":1782979458624}

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. ), 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 ( from a workbench, no portless) is unaffected. Prerequisites - installed - installed globally ( or via Homebrew). Verify with . - Repo bootstrapped: . The first run on a fresh worktree mu…