
Pitchfork
- 1 installs
- Updated June 16, 2026
- knowsuchagency/pitchfork-skill
Guides use of pitchfork, a daemon/process manager for running, supervising, and logging background dev services via pitchfork.toml and a shell hook.
About
A skill covering pitchfork, a Rust daemon/process manager for developers that runs and supervises background dev services. A developer uses it to start, stop, and log long-running local processes as a lighter alternative to pm2 or systemd.
- Manages background dev daemons defined in pitchfork.toml with ready checks
- Shell hook auto starts/stops daemons on entering/leaving a project directory
Pitchfork by the numbers
- 1 all-time installs (skills.sh)
- Ranked #468 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/knowsuchagency/pitchfork-skill --skill pitchforkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| Last updated | June 16, 2026 |
| Repository | knowsuchagency/pitchfork-skill ↗ |
What it does
Guides use of pitchfork, a daemon/process manager for running, supervising, and logging background dev services via pitchfork.toml and a shell hook.
Files
Pitchfork
Pitchfork is a "devilishly good" daemon/process manager for developers, written in Rust by jdx (author of mise). It starts a background service only if it isn't already running (no duplicate processes), auto-restarts on failure with backoff, resolves dependency chains, supports ready checks/file-watching/cron, and can auto start/stop daemons as you cd in and out of a project via a shell hook. Monitor through a terminal TUI or web dashboard, and drive it from AI assistants through a built-in MCP server.
- Docs: https://pitchfork.jdx.dev
- Repo: https://github.com/jdx/pitchfork
- CLI reference: https://pitchfork.jdx.dev/cli.html
- Config reference: https://pitchfork.jdx.dev/reference/configuration.html
Tip: run pitchfork <command> --help for the exact, version-specific flags on any subcommand.This skill documents pitchfork ≥ 2.10.
When to reach for pitchfork
- You keep starting the same dev server twice and leaking orphan processes — pitchfork refuses to
start a daemon that's already running.
- You want services to come up automatically when you
cdinto a project and shut down when you
leave (the shell hook).
- You have a stack (db → cache → api → worker) with startup ordering and readiness gating.
- You want pm2/foreman/overmind/systemd ergonomics for local dev without the weight.
Installation
mise use -g pitchfork # Recommended (jdx's own tool)
cargo install pitchfork-cli # From crates.io
brew install pitchfork # Homebrew
# or download a prebuilt binary from https://github.com/jdx/pitchfork/releases
pitchfork --version # VerifyShell activation (the shell hook — enables auto start/stop)
The shell hook is what makes pitchfork "automatically start daemons when you enter a project directory and stop them when you leave." It hooks your shell's directory-change so that, on each cd, pitchfork reconciles the daemons declared in the nearest pitchfork.toml. Add the activation line to your shell config, then restart your shell:
echo 'eval "$(pitchfork activate bash)"' >> ~/.bashrc # Bash
echo 'eval "$(pitchfork activate zsh)"' >> ~/.zshrc # Zsh
echo 'pitchfork activate fish | source' >> ~/.config/fish/config.fish # FishSupported shells: bash, zsh, fish. See https://pitchfork.jdx.dev/guides/shell-hook.html.
How the hook behaves (per the shell-hook guide):
- On entering a directory, daemons whose config includes
auto = ["start"]are started. - On leaving, daemons with
auto = ["stop"]are stopped — but only after a brief delay (so a
quick cd out && cd back doesn't bounce the service), and only once no terminal sessions remain inside the project directory. Open another terminal still cd'd into the project and the service keeps running.
Tab completions (optional)
pitchfork completion bash > ~/.local/share/bash-completion/completions/pitchfork
pitchfork completion zsh > ~/.zfunc/_pitchfork
pitchfork completion fish > ~/.config/fish/completions/pitchfork.fishStart at boot (optional)
boot registers the supervisor with the OS init system (launchd on macOS, systemd on Linux), so daemons marked boot_start = true come up at login/startup.
pitchfork boot enable # User-level (~/Library/LaunchAgents or ~/.config/systemd/user)
sudo pitchfork boot enable # System-level, for all users (/Library/LaunchDaemons or /etc/systemd/system)
pitchfork boot status
pitchfork boot disableCore commands
pitchfork run <ID> -- <CMD>... # Run an ad-hoc command in the background under name ID (no toml needed)
pitchfork start [ID...] # Start daemon(s) defined in pitchfork.toml; -l/-g/-a for local/global/all
pitchfork stop [ID...] # Stop daemon(s); -a/-l/-g; graceful SIGTERM then SIGKILL
pitchfork restart <ID> # Stop then start a daemon
pitchfork status <ID> # Detailed status of one daemon (PID, status)
pitchfork list # Table of all daemons + state (alias: ls; --hide-header to omit header)
pitchfork logs [ID...] # Show/tail logs; omit ID for all daemons
pitchfork wait <ID> # Block until the daemon STOPS, tailing its logs (exits with its code)
pitchfork enable <ID> # Allow a daemon to start (undo disable)
pitchfork disable <ID> # Prevent a daemon from starting/restarting
pitchfork clean # Drop stopped/failed entries from the list (alias: c)
pitchfork tui # Interactive terminal dashboardMost commands have short aliases: r run, s start, kill stop, ls list, l logs, w wait, e enable, d disable, c clean, stat status, cfg config, sup supervisor.
run — one-off background daemons (no config file)
run is the fastest way to background a command. The name (<ID>) makes it idempotent and addressable by every other command.
pitchfork run web -- npm run dev # Background a dev server named "web"
pitchfork run api -- ./server --port 8080
pitchfork run api -f -- ./server # -f/--force: restart if "api" already running
pitchfork run api --retry 3 -- ./server # Restart up to 3 times on failure
pitchfork run api -d 5 -- ./server # Consider ready after a 5s delay (default 3s)
pitchfork run api -o 'Listening on' -- ./server # Ready when stdout matches this regex
pitchfork run api --http http://localhost:8080/health -- ./server # Ready on HTTP 2xx
pitchfork run api --port 8080 -- ./server # Ready when TCP port 8080 is listening
pitchfork run api --cmd 'pg_isready' -- ./server # Ready when this shell command exits 0
pitchfork run web --expected-port 3000 --bump -- npm run dev # Find a free port if 3000 is taken
pitchfork run build -q -- ./build.sh # -q/--quiet: suppress startup log outputEverything after -- is the command and its args, run verbatim — no shell parsing surprises.start / stop — daemons from pitchfork.toml
pitchfork start api # Start one daemon defined in pitchfork.toml
pitchfork start api worker # Start several
pitchfork start -l # All LOCAL daemons in ./pitchfork.toml (alias --all-local)
pitchfork start -g # All GLOBAL daemons in ~/.config/pitchfork/config.toml (--all-global)
pitchfork start -a # All daemons, local and global
pitchfork start api -f # Force restart if already running
pitchfork start api --port 8080 # Override/add a ready check at start time
pitchfork stop -a # Stop everything in reverse dependency orderstart waits for each daemon's ready check to pass before returning. stop does a graceful shutdown: SIGTERM, wait ~3s (fast polling), then SIGKILL if still alive. With -a/-l/-g, daemons stop in reverse dependency order (dependents before their dependencies).
logs — view and follow output
pitchfork logs api # All logs for "api" (paged if long)
pitchfork logs api worker # Multiple daemons
pitchfork logs # All daemons
pitchfork logs api -n 50 # Last 50 lines
pitchfork logs api -f # Follow in real time (alias --tail/--follow)
pitchfork logs api --since 5min # Relative window: 5min, 2h, 1d
pitchfork logs api --since '10:30' --until '12:00' # Time window (today)
pitchfork logs api --since '2024-01-15 10:00:00' # Absolute datetime
pitchfork logs api --raw -n 100 # Raw lines, no color/formatting (good for piping)
pitchfork logs api --clear # Delete logs for "api" (pitchfork logs --clear = all)Logs are stored under the pitchfork state dir (e.g. ~/.local/state/pitchfork/logs).
wait — block until a daemon stops
wait blocks until the daemon exits, streaming its logs, and returns the daemon's exit code. Use it to chain on a one-shot daemon or keep a script attached to a long-running one:
pitchfork wait migrate && echo "migration done"Note: wait waits for the daemon to stop. To block until a service is ready and thencontinue, use a ready check (--port,--http,--output,--cmd,--delay) onrun/start,
which already blocks until ready before returning.
Configuration: pitchfork.toml
Define daemons in a pitchfork.toml at your project root. Each [daemons.<id>] block describes one service. A config.toml in ~/.config/pitchfork/ (or /etc/pitchfork/) holds global daemons available everywhere.
# Optional: prefix this file's daemon names, e.g. referenced as "frontend/web"
namespace = "frontend"
[daemons.postgres]
run = "postgres -D ./data"
auto = ["start", "stop"] # auto-start on cd-in, auto-stop on cd-out (needs the shell hook)
ready_port = 5432 # ready when this TCP port accepts connections
dir = "db" # working directory (relative to the toml, or absolute)
[daemons.redis]
run = "redis-server"
auto = ["start", "stop"]
ready_output = "Ready to accept connections" # ready when stdout matches this regex
[daemons.api]
run = "npm run dev:api"
depends = ["postgres", "redis"] # started after deps are ready (topological; independents in parallel)
ready_http = "http://localhost:8080/health" # ready when this URL returns 2xx
retry = 3 # auto-restart up to 3 times on failure (true = unlimited)
watch = ["src/**/*.ts"] # restart when matching files change
env = { NODE_ENV = "development", PORT = "8080" }
[daemons.worker]
run = "npm run worker"
depends = ["redis"]
ready_delay = 2 # just wait N seconds, then consider ready
[daemons.cleanup]
run = "./scripts/cleanup.sh"
cron = "0 0 2 * * *" # run on a 6-field cron schedule (sec min hour dom mon dow): 2am dailyDaemon keys
| Key | Type | Purpose |
|---|---|---|
run | string (required) | The command to execute. |
dir | string | Working directory (relative to the toml, or absolute). |
env | table | Environment variables, e.g. { NODE_ENV = "development" }. |
user | string | Run as this user/uid (e.g. "postgres" or "501"); needs privileges. |
auto | array | Shell-hook lifecycle: "start" (on cd-in), "stop" (on cd-out). |
depends | array | Daemons that must be ready first; topological, independents run in parallel. Cross-namespace via "global/postgres". |
retry | int or bool | Auto-restart on failure: a count, or true for unlimited (with backoff). |
boot_start | bool | Start this daemon when the supervisor starts at boot. |
mise | bool | Run the command through mise (load mise.toml tools/env first). |
| Ready checks | (pick one; all block start/run until satisfied) | |
ready_delay | int | Seconds to wait, then mark ready. |
ready_output | string | Regex; ready when daemon output matches. |
ready_http | string or table | URL that must return 2xx, or { url = "...", status = [200, 401] }. |
ready_port | int | TCP port that must be listening. |
ready_cmd | string | Shell command polled until it exits 0. |
| File watching | ||
watch | array | Glob patterns; matching changes trigger a restart. |
watch_mode | string | "native", "poll", or "auto" (default). |
| Ports / proxy | ||
port | int / array / table | Expected port(s); table form { expect = [3000], bump = 10 } auto-finds a free port. |
| Scheduling | ||
cron | string or table | 6-field cron (sec min hour dom mon dow), or { schedule, retrigger, immediate }. |
| Resource limits | ||
memory_limit | string | e.g. "512MB", "2GiB" — restart/kill if exceeded. |
cpu_limit | number | CPU percent cap (100 = one core; 200 = two). |
stop_signal | string or table | Signal for graceful stop, e.g. "SIGINT" or { signal = "SIGINT", timeout = "5s" }. |
Deprecated port keys you may see in older configs:expected_port,auto_bump_port,
port_bump_attempts. Prefer the unifiedportkey.
Lifecycle hooks — [daemons.<id>.hooks]
Run commands at lifecycle transitions (see https://pitchfork.jdx.dev/guides/lifecycle-hooks.html):
[daemons.api]
run = "npm run dev:api"
ready_http = "http://localhost:8080/health"
[daemons.api.hooks]
on_ready = "curl -X POST https://alerts.example.com/ready" # daemon passed its ready check
on_fail = "./scripts/cleanup.sh" # daemon failed
on_retry = "echo 'retrying...'" # before each retry attempt
on_stop = "./cleanup.sh" # explicitly stopped by pitchfork
on_exit = "./teardown.sh" # any termination (clean exit, crash, or stop)
# on_output supports filtering: { run = "...", regex = "ERROR", debounce = "2s" }Cron scheduling
cron uses a 6-field expression: second minute hour day-of-month month day-of-week (Sunday = 0). The inline-table form adds a retrigger policy:
[daemons.report]
run = "./generate-report.sh"
cron = { schedule = "0 0 2 * * *", retrigger = "finish", immediate = false }retrigger controls what happens when the schedule fires while the previous run is still active:
| Value | Behavior |
|---|---|
finish (default) | Only retrigger once the previous run has finished — no overlap. |
always | Kill the running instance and start fresh — always run the latest. |
success | Only retrigger if the previous run exited 0. |
fail | Only retrigger if the previous run failed (auto-retry logic). |
See https://pitchfork.jdx.dev/guides/scheduling.html.
Groups
Group daemons to start/stop several at once:
[groups.backend]
daemons = ["postgres", "redis", "api", "worker"]pitchfork start backend # start the whole groupManaging config from the CLI
config add writes a [daemons.<id>] block into the nearest pitchfork.toml:
pitchfork config # List all pitchfork.toml files from the cwd upward
pitchfork config add api -- npm run dev # Command after --
pitchfork config add api --run 'npm start' --retry 3
pitchfork config add api --run 'npm start' --watch 'src/**/*.ts'
pitchfork config add api --run 'npm start' --autostart --autostop # sets auto = ["start","stop"]
pitchfork config add worker --run './worker' --depends api
pitchfork config add report --run './report.sh' \
--cron-schedule '0 0 2 * * *' --cron-retrigger finish
pitchfork config add api --run './server' \
--ready-http http://localhost:8080/health # also: --ready-output, --port, --ready-cmd, --delay
pitchfork config add api --run './server' --on-ready 'curl .../ready' --on-fail './cleanup.sh'
pitchfork config remove api # Remove a daemon (alias: rm)Namespaced ids are supported, e.g. pitchfork config add frontend/api ....
Auto start/stop (the day-to-day workflow)
With the shell hook installed (pitchfork activate), mark daemons auto = ["start", "stop"]. Entering the project directory starts them; leaving stops them (after a short delay, once no terminals remain in the dir). Services come up exactly when you're working and clean up when you move on — no manual start/stop needed.
[daemons.web]
run = "npm run dev"
auto = ["start", "stop"]
ready_port = 3000Use auto = ["start"] alone to start on entry but leave it running, or auto = ["stop"] to only clean up on exit.
Reverse proxy — stable *.localhost URLs
The proxy maps a stable slug URL (e.g. https://myapp.localhost) to a daemon's actual port, so bookmarks and configs don't break when a port changes or gets bumped. Slugs live in the global config under [slugs].
pitchfork proxy trust # Install the proxy's self-signed TLS cert into the system trust store
pitchfork proxy add ... # Map a slug → project dir + daemon
pitchfork proxy status # Show all slugs and their state
pitchfork proxy remove ... # (alias: rm)Enable it in config:
[settings.proxy]
enable = trueTUI dashboard
pitchfork tui # Interactive terminal dashboard (start/stop/restart, view logs, vim-style keys)pitchfork list gives a quick text overview; pitchfork logs -f follows output. A browser-based web dashboard is also available — see https://pitchfork.jdx.dev/guides/tui.html.
Supervisor
A background supervisor process tracks daemons and performs restarts/scheduling/file-watching. The shell hook and boot enable manage it for you, but you can drive it directly:
pitchfork supervisor start # Start the supervisor in the background
pitchfork supervisor status
pitchfork supervisor stop
pitchfork supervisor run # Run it in the foreground (debugging)Container mode
For Docker/CI, pitchfork can run as PID 1 / foreground supervisor that brings up the whole stack and keeps the container alive. See https://pitchfork.jdx.dev/guides/container-mode.html.
MCP server (AI assistants)
Pitchfork ships an MCP server so assistants like Claude Code and Cursor can manage daemons directly:
pitchfork mcp # Speaks MCP over stdin/stdout; wire into your assistant's MCP configExample Claude Code registration:
claude mcp add pitchfork -- pitchfork mcpExposed tools: pitchfork_status (list daemons + PID/status/errors), pitchfork_start (with force to restart), pitchfork_stop, pitchfork_restart, and pitchfork_logs (n lines, optional daemon ids). See https://pitchfork.jdx.dev/guides/mcp.html.
Quick recipes
Background a dev server and follow it:
pitchfork run web -- npm run dev
pitchfork logs web -fA full stack with ordering and readiness, auto-managed on cd:
[daemons.db]
run = "docker compose up postgres"
auto = ["start", "stop"]
ready_port = 5432
[daemons.api]
run = "npm run dev:api"
depends = ["db"]
auto = ["start", "stop"]
ready_http = "http://localhost:8080/health"
retry = 3
watch = ["src/**/*.ts"]
[daemons.web]
run = "npm run dev:web"
depends = ["api"]
auto = ["start", "stop"]
ready_port = 3000Then just cd into the project (with the shell hook active) and everything comes up in order; cd away and it tears down.
Tips & gotchas
- Pitchfork won't start a daemon that's already running — re-running
start/runis safe and
idempotent (it's how you avoid duplicate dev servers).
- Prefer
depends+ aready_*check over manual ordering orsleep— pitchfork blocks on
readiness and parallelizes independent daemons.
ready_*blocks until ready;waitblocks until stopped. Don't confuse them.- The shell hook only stops a daemon once no terminal sessions remain in the project dir — a
second terminal still in the directory keeps services alive.
- Cron is 6 fields (leading seconds), unlike classic 5-field crontab.
pitchfork cleanclears stale stopped/failed rows fromlist; it never touches running daemons
or your config.
- Global daemons live in
~/.config/pitchfork/config.toml; reference them across projects as
global/<name> (or your configured namespace) in depends/groups.
- Run
pitchfork <command> --helpfor exact, version-specific flags.
.DS_Store
*.log
MIT License
Copyright (c) 2026 knowsuchagency
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
pitchfork-skill
A comprehensive Agent Skill for **pitchfork** — the "devilishly good" daemon/process manager for developers, by jdx (author of mise).
The skill teaches Claude (and other skill-compatible assistants) how to manage local background services with pitchfork: running ad-hoc daemons, authoring pitchfork.toml, the shell hook for auto start/stop on cd, ready checks, file-watch restarts, cron scheduling, dependency chains and groups, lifecycle hooks, the reverse proxy, the TUI, the supervisor, and the MCP server.
What's here
- [`SKILL.md`](./SKILL.md) — the skill itself (this is the whole thing).
Install
Claude Code
Copy the directory into your skills folder:
git clone https://github.com/knowsuchagency/pitchfork-skill.git
mkdir -p ~/.claude/skills/pitchfork
cp pitchfork-skill/SKILL.md ~/.claude/skills/pitchfork/SKILL.mdOr symlink it so updates flow through:
ln -s "$PWD/pitchfork-skill" ~/.claude/skills/pitchforkThen ask Claude anything about pitchfork and the skill activates automatically.
About pitchfork
- Docs: https://pitchfork.jdx.dev
- Repo: https://github.com/jdx/pitchfork
mise use -g pitchfork # install (recommended)
pitchfork run web -- npm run dev
pitchfork logs web -fLicense
MIT — see `LICENSE`. This skill is an independent community resource and is not affiliated with or endorsed by the pitchfork project.