Hum runs your project so you and your agents can work together.
Agents can query your local server logs, view readiness and restart the processes while you also watch the logs. Compatible with worktrees and parallel work.
hum.yaml ──> hum daemon ──> db ──> api ──> web
│
└── bounded logs <── CLI / coding agents
Terminal panes make processes visible. Hum makes them queryable:
hum up --detach
hum status api --json
hum logs api --stream stderr --tail 50 --json
hum wait api --match "ready" --timeout 30s --jsonInstead of parsing a terminal buffer, tools get explicit lifecycle state, bounded structured output, readiness, and stable cursors. Herdr can still provide the panes—its Hum plugin uses Herdr for UI and Hum for process state.
Run a process and follow its retained logs
Start and stop a dependency-ordered stack
Let a coding agent diagnose a failed process from its retained logs
Install on macOS with Homebrew:
brew trust --formula brettinternet/tap/hum
brew install brettinternet/tap/hum
man humHomebrew installs the generated hum(1) manual. Release archives also include hum.1 for other package integrations.
Or download and verify the latest macOS or Linux release directly:
curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/brettinternet/hum/main/install.sh | shSet HUM_VERSION=0.9.0 (with or without the leading v) to pin a release, or set
HUM_INSTALL_DIR to install somewhere other than $HOME/.local/bin.
Or install releases with mise:
[tools]
"github:brettinternet/hum" = "latest"To build from a checkout, see development setup.
Try a portable clock process in a directory:
mkdir hum-quickstart && cd hum-quickstart
git init -q
cat > hum.yaml <<'YAML'
version: 1
processes:
clock:
argv: [sh, -c, "while :; do date; sleep 1; done"]
YAML
hum run hello --detach -- sh -c 'printf "hello from hum\\n"'
hum uphum up follows output. Press Ctrl+C to detach, then stop:
hum downSchemaStore-aware editors load hum.schema.json automatically for hum.yaml and
alternate filenames matching hum.*.yaml, *.hum.yaml, hum.yml, or *.hum.yml. hum init also
adds an inline schema directive. Other editors can select the root schema manually. The Go manifest
parser remains authoritative.
Without hum.yaml, hum up finds conventional dev tasks in Mise, Task, Just, Make,
package.json, Deno, Composer, bin/dev, and Mix projects with a literal Phoenix dependency.
For multiple processes, add hum.yaml:
version: 1
processes:
db:
argv: [docker, compose, up, db]
ready:
match: "ready"
api:
argv: [bun, run, api]
after: [db]
ready:
match: "Listening"
web:
argv: [bun, run, dev]
after: [api]
ready:
match: "Local:"hum up starts in dependency order and follows output. Once startup completes, Ctrl+C detaches
and hum down stops; the daemon owns the processes, so closing the follower never kills them.
Ctrl+C during startup aborts instead and stops what that hum up launched.
Use hum up --detach to wait and return, or hum up --full for full readiness details.
For checks that do not emit a reliable startup message, use an executable probe. Exit status 0 marks the process ready. For example, check PostgreSQL inside Docker Compose:
ready:
exec: [docker, compose, exec, -T, db, pg_isready, -U, postgres]
interval: 1s
timeout: 30sOr check an HTTP readiness endpoint:
ready:
exec: [curl, --fail, --silent, --show-error, "http://127.0.0.1:3000/readyz"]
interval: 1s
timeout: 30sJSON and redirected output stay bounded:
processes:
web:
argv: [task, "dev:web"]
ready:
match: "Listening on"hum up
hum up --detach
hum up --full
hum status
hum status web
hum logs --follow
hum start web
hum stop web
hum downhum status shows a compact project overview. hum status NAME adds readiness and diagnostics.
hum start NAME does not start dependencies. ready.exec runs exact argv without a shell, inherits
cwd/env, and retries every second by default. It gates startup, not liveness. hum down stops project
processes concurrently. See design and command semantics.
Load shared values from files, then override or remove them per process:
version: 1
environment:
files: [.env]
processes:
api:
argv: [bun, run, api]
env:
PORT: "3001"
LEGACY_DATABASE_URL: null# .env
DATABASE_URL=postgres://localhost/app
LEGACY_DATABASE_URL=postgres://localhost/old
export PORT = "3000" # overridden by api.env
LITERAL_DOLLAR='$NAME'The result for api includes PORT=3001 and no LEGACY_DATABASE_URL:
caller environment → .env → processes.api.env
inherit: falseskips the caller environment.envaccepts strings ornull; quote numeric and boolean YAML values.- Every file is required, relative to the selected manifest, and inside the project root.
- Files accept UTF-8 assignments, comments,
export, whitespace, and whole quoted values. - Hum does not discover files or expand
$NAME,${...},$(), or backticks. Single-quote literal forms or use an external loader.
No configuration preserves the caller environment exactly. start, up, declared run, and
restart load files before daemon contact; ordinary read-only commands do not. hum doctor loads
and validates them without exposing values or launching anything. Processes and automatic relaunches
keep their launch snapshot. Use hum restart NAME to reload changes.
Limits: 16 files; 1 MiB and 4,096 assignments per file; 4 MiB per environment; 8 MiB per encoded request. Environment metadata stays private, but child and probe output is unredacted. Do not print secrets.
Use --project DIR or -C DIR before or after the subcommand:
hum --project /path/to/checkout up
hum status -C ../checkout api
hum run preview --project /path/to/checkout -- bun run previewSelect an alternate manifest with --file PATH or -F PATH:
hum -F hum.dev.yaml up
hum restart -F hum.test.yaml api- A relative selector starts from the invocation directory.
- Ad-hoc runs use the selected directory; manifest
cwdstays project-relative. --filemust name a regular file inside the project.- Without
--file, Hum useshum.yaml, or conventional discovery when it is absent. - All manifests share one project namespace. The same process name cannot run twice through separate files.
- Runtime-only commands use
--fileonly to identify the project. --fileis unavailable onversion,serve,shutdown,mcp, andskill.
Run hum doctor before launching work to check the selected project, Hum settings, runtime path,
manifest environments, process and ready.exec executables, and any already-present daemon. It never
starts the daemon, executes a process or readiness probe, repairs files, or retains state; an absent
daemon is informational. Human output ends with PASS/WARN/FAIL/INFO counts. --json emits one
schema-versioned object, and the command exits 1 only when a check fails or usage is invalid.
hum init creates only hum.yaml; Hum does not merge manifests or support overlays. -d means
--detach for daemon, run, and up.
Run a named process without a manifest:
hum run preview -- bun run preview
hum run preview --detach -- bun run preview
hum attach preview
hum logs preview --follow --tail 0
hum wait preview --match "ready"
hum stop preview # preserve state
hum remove preview # discard state
hum remove --all # discard sessions in this scopePut scope selectors before the child --. Foreground runs propagate exit status, stop on Ctrl+C
or SIGTERM, and detach on SIGHUP. Detached runs belong to the daemon; hum attach NAME and
hum logs --follow only observe them.
Manifest processes default to never. Enable bounded recovery:
processes:
api:
argv: [bun, run, api]
restart: on-failureA non-zero exit retries after 1s, 2s, 4s, 8s, and 16s, then stops. Manual controls win.
Automatic relaunches reuse the previous definition; hum restart NAME adopts manifest changes.
Status, JSON, and MCP show recovery state and counts.
For stop_grace:
- Omit it to inherit the daemon default.
- Use
0sfor an immediate kill. - Restarts adopt changes; automatic relaunches and orphan reclaim keep the previous policy.
- Status, JSON, and MCP show the effective value and whether it was inherited.
hum logs --follow # declared processes
hum logs web worker --tail 50 # selected processes
hum logs web --stream stdout --match Listening # matching stdout
hum logs web --stream system # supervision eventsAd-hoc sessions are selected by name. The default stream, both, includes stdout, stderr, and
system events. --after-cursor pages from the oldest retained entry; otherwise logs start with the
newest default window. Ctrl+C closes only the follower.
Human output uses [NAME] prefixes; JSON uses named NDJSON events. Logs next is the consumed cursor.
A process next_cursor is the next cursor to assign.
Every supported CLI --json result and NDJSON record includes schema_version: 1.
hum doctor --json is the read-only preflight result for automation. See the
version 1 CLI machine-output contract for covered commands, required and
optional fields, framing, ordering, exit-code interaction, Compatibility rules, and the boundary
from Hum's private daemon protocol. Attached hum run remains raw child output and does not use the CLI JSON contract.
Completion is opt-in and does not start a daemon:
# bash
source <(hum completion bash)
# zsh
source <(hum completion zsh)
# fish
hum completion fish > ~/.config/fish/completions/hum.fishDetect Hum's CLI machine-output contract before relying on JSON field semantics:
hum version --json
# {"schema_version":1,"version":"<version>","build_time":"<time>"}This feature-detection call does not resolve a project or contact the daemon.
With hum and Python 3.10+ on Herdr's PATH, install the process picker:
herdr plugin install brettinternet/hum/plugins/herdr --yesThe picker uses the public CLI contract to open followed logs or interactive attachments for the selected workspace. See the Herdr plugin guide for actions and ownership.
With hum on PATH, install the Claude Code plugin:
claude plugin marketplace add brettinternet/hum
claude plugin install hum@humInstall the Codex plugin from a checkout with hum on PATH:
codex plugin marketplace add .
codex plugin add hum@humThe plugins bundle the hum skill and MCP registration. If plugin installation is
unavailable, register hum mcp manually as described in the
coding-agent setup.
hum mcp exposes project processes, bounded output, and one-shot TTY input.
// .mcp.json
{
"mcpServers": {
"hum": {
"command": "hum",
"args": ["mcp"]
}
}
}Separate worktrees run independently:
cd .worktrees/agent-a
hum up --detach
cd .worktrees/agent-b
hum up --detach
hum list --all
hum --project .worktrees/agent-a downSee coding-agent setup for Claude Code, Cursor, MCP, and the shell-only skill.
Enable TTY support per process:
processes:
console:
argv: [./console]
tty: truehum run console --tty -- ./console
hum logs console
hum input console --text 'value'
hum input console --base64 PADDED_VALUEEach TTY has one input owner. Input is sent once, never queued or echoed. logs --follow receives output only. --json and MCP expose the same operation result.
hum status # nearest Git root
hum -C ../other-worktree status # another project, even if removed
hum list # compact name, state, and PID
hum list --full # all human-readable process details
hum list --all # every scope; combine with --full if needed
hum -g run proxy -- caddy run # machine-wide ad-hoc session
hum signal proxy HUP --global # global selector after positionalshum ls is an interactive alias for hum list; documentation and scripts use the canonical name.
Project roots are canonical: symlink aliases share a scope, while separate worktrees do not. Use
--project PATH or -C PATH
to select another project. Use --global or -g only for machine-wide ad-hoc sessions, before
run's child --.
--global conflicts with --project and list --all; init and up reject it. JSON reports
scope as project or global; global records omit project_root.
hum events [NAME...] reads recent durable service history without requiring a manifest or a
running daemon. Use repeatable --kind, --failed, --match, --since, --tail, and
--after-cursor to narrow bounded pages; --json emits schema-versioned event records and trailing
cursor metadata. Human output fits the terminal width (80 columns when unknown), elides detail first,
and colors only semantic event words under the usual TTY/TERM/NO_COLOR policy; --full prints
complete multi-line details. History is private, retained to 2,000 events or 1 MiB per scope, and
excludes child output, environment, and input. It survives daemon replacement but not runtime-directory
cleanup. An unreadable cursor high-water mark makes that scope's history unavailable rather than
reusing cursors; service control remains available.


