Launch long-running background processes (web servers, LLM proxies, training jobs, eval runs, daemons, watchers) as live, user-controllable foreground processes inside a dedicated Herdr "background" workspace. Creates the workspace and {purpose}_N tabs automatically, places each process in a pane (max 4 per tab in a 2x2 grid, then a new tab), names panes with their listening ports, tees all output to logs/<purpose>/, and records every launch in a global registry. Use whenever the user asks to start, run, launch, or deploy any server, service, training, or eval process that should keep running where they can watch and control it (启动服务, 后台跑训练, 部署代理). Not for one-shot commands — use plain Bash for those.
Resources
4Install
npx skillscat add yhj137/herdr-run Install via the SkillsCat registry.
herdr-run
Launch a persistent process the way the user actually wants long-running work to run: visible in a terminal pane, running in the foreground of that pane, logging to disk, and interruptible with Ctrl-C — not hidden in a detached shell. The herdr_run.py script does all placement work in one call; never orchestrate tabs/panes by hand for this.
When to use
Use when launching anything that keeps running and matters over time: API/LLM proxy servers, web servers, training jobs (SFT/RL/rollout), evaluation runs, daemons, file watchers, notebooks.
Do not use for: one-shot commands (tests, builds, greps), anything expected to finish in seconds, or work the user asked to keep inside the current pane. For general herdr inspection/control unrelated to launching processes, the plain herdr skill applies instead.
Quick start
python3 <skill-dir>/scripts/herdr_run.py launch <purpose> "<command>" [--note "..."] [--port N]
python3 <skill-dir>/scripts/herdr_run.py list # what's running, where<skill-dir> is this skill's base directory (shown when the skill loads). Example:
python3 ~/.claude/skills/herdr-run/scripts/herdr_run.py launch llm_proxy \
"uv run python -m proxy.server --port 7890" \
--port 7890 --note "vllm api proxy"The script prints JSON with pane_id, tab_label, log, and follow-up command hints. Use those IDs verbatim for everything afterwards.
Purpose names are a shared registry — check before you create
Purpose names (the llm_proxy in llm_proxy_1) are the vocabulary the user sees in tabs and log directories forever. They are the user's namespace, not yours to invent freely. Protocol:
- Run
herdr_run.py list --purposesto see the registered purposes and how many launches each has. - Reuse an existing purpose when it is the same kind of process — a second proxy server is another
llm_proxylaunch, notproxy2. - For a genuinely new kind of process, ask the user to name it (or propose a name and get their nod — if they already named it in their request, that counts). Then launch with
--new-purpose. - The script rejects unknown purposes without
--new-purpose, listing what is registered — treat that error as "go ask", never as "find a way around".
The registry has no file of its own: known purposes are derived from the launch registry (<skill>/data/launches.jsonl, override with HERDR_RUN_RECORD_FILE / --record-file) plus every live {purpose}_n tab. Editing that JSONL edits the vocabulary.
What you must decide before launching
- purpose — a short slug for the kind of process:
llm_proxy,rollout,sft,eval. It names the tab (llm_proxy_1) and the log directory. Lowercase[a-z0-9_-]. - --note — a human-readable phrase for the pane title, e.g. "vllm api proxy". Defaults to the first words of the command; a good note is better.
- --port N — required whenever the process listens on a port. Scan the command for it:
--port N,-p N,PORT=N,--listen,:8xxxin a URL. The port is appended to the pane name (vllm api proxy:7890) so the user can spot conflicts at a glance. Repeat the flag for multiple ports. If you discover the port only after startup, rename:herdr pane rename <pane_id> "<label>:<port>". - --cwd — where the command runs; defaults to the current working directory. Pass it explicitly when the process must run from its project root and you are elsewhere.
- --focus — only when the user asked to watch it start; default keeps their focus untouched.
Verify, follow, and control
After launching, confirm the process came up instead of assuming:
herdr pane wait-output <pane_id> --match "Uvicorn running" --timeout 60000
herdr pane read <pane_id> --source recent-unwrappedFor servers, prefer verifying by connectivity over waiting for a startup banner: many programs print no banner at all (Python 3.14's http.server doesn't) or emit it on stderr. Both stdout and stderr are already tee'd to the log. If the first check races the startup, read the pane once, then re-check.
curl -s localhost:<port>/ # any platform
lsof -nP -iTCP:<port> -sTCP:LISTEN # macOS / Linux
Get-NetTCPConnection -LocalPort <port> -State Listen # Windows PowerShellNote: on herdr 0.8.2 pane read --lines N returns empty output — do not pass --lines. Output longer than the pane's screen is in the log file anyway.
Everything the process prints is also on disk at the reported log path — prefer reading the file for long output. To stop a process, send it a Ctrl-C (it is a foreground process in that pane):
herdr pane send-keys <pane_id> ctrl+cTo see everything currently running (and recently finished), read the joined view instead of raw files:
python3 <skill-dir>/scripts/herdr_run.py list # live processes
python3 <skill-dir>/scripts/herdr_run.py list --history # everything ever
python3 <skill-dir>/scripts/herdr_run.py list --purposes # registered purposes
python3 <skill-dir>/scripts/herdr_run.py list --json # machine-readableEach row joins a registry entry with live herdr state: running (foreground process in pane), idle (pane back at shell — process exited), superseded (pane reused by a newer launch), gone (pane closed). It also flags untracked panes that live in the background workspace but were not launched through this skill.
Placement rules (what the user sees)
- Everything lands in one workspace labeled
background, created on first use. - Tabs are named
{purpose}_{n}. A tab holds at most 4 panes in a 2x2 grid; the 5th process of the same purpose opens{purpose}_{n+1}. - The pane title is the note plus ports. Idle-looking placement is handled automatically — do not pre-split panes yourself.
Options
| Option | Default | Meaning |
|---|---|---|
--note TEXT |
first words of command | pane title; also the log filename slug |
--port N (repeatable) |
none | listening port(s), shown in pane title |
--cwd PATH |
current dir | working directory of the process |
--log-dir PATH |
<cwd>/logs |
log root; file lands in <log-dir>/<purpose>/<yymmdd-hhmmss-note>.log (-2 suffix on same-second collisions) |
--new-purpose |
off | register a genuinely new purpose — only after asking the user |
--workspace-label |
background (env HERDR_RUN_WORKSPACE) |
target workspace |
--record-file PATH |
<skill>/data/launches.jsonl (env HERDR_RUN_RECORD_FILE) |
global registry |
--no-record |
off | skip the registry entry (the purpose then only stays known via its live tabs) |
--focus |
off | focus the new tab |
--dry-run |
off | print the placement plan, change nothing |
list options: --purposes (purpose registry instead of processes), --history (include exited/superseded), --json, plus the same --workspace-label / --record-file.
Edge cases
- Very long or heavily-quoted commands (heredocs, multi-line): write them to a script file (e.g.
run_server.sh) and launchbash run_server.shinstead —pane runtypes the command into a real shell. - Launching several processes of the same purpose: do it sequentially, one script call at a time — placement is decided from live tab/pane state, so parallel same-purpose launches can race and land in the wrong tab. Different purposes never collide and may run in parallel.
- Interactive prompts (confirmation, login) will block the pane; read the pane and answer via
herdr pane send-text <pane_id> "<answer>"or pick non-interactive flags upfront. - Processes that daemonize themselves (
&,nohup, daemon mode) defeat the design — the pane would go idle while the real process hides. Strip backgrounding and let the pane be the process's lifetime. - The script refuses nothing silently: any herdr error is reported with the failing command.
--dry-runis cheap — use it when unsure about placement. - Platforms: macOS, Linux, and Windows all work. The tee pipeline is generated per platform — POSIX shells get
… 2>&1 | tee -a, Windows PowerShell panes getTee-Object -Appendwith PS quoting automatically; you never need to care which.