Terminal-based project manager built around tmux and git worktrees.
pm gives every feature its own git branch, worktree, and tmux session, and optionally a team of Claude Code agents that talk to each other through a file-based message queue. You dispatch work; the agents implement, review, and report back in their own sessions.
Every command supports --help for its full flag reference — this README
covers the mental model and the parts you can't get from --help.
- tmux
- git
- gh — for the PR/review/sync commands (
pm feat pr,pm feat review,pm feat sync)
cargo install --path .Installs the pm binary to ~/.cargo/bin/ (ensure it's on your PATH).
Create a project — three ways, pick one:
pm init ~/projects/myapp # new repo
pm init ~/projects/myapp --git https://github.com/org/myapp.git # clone
pm register ~/code/myapp --name myapp # adopt an existing repo (--move to restructure in place)Each gives you a project root with the repo in main/, a .pm/ state
directory, and the harness's hooks in main/.claude/. Bundled skills, agent
definitions, workflows, and the baseline install once per machine into the
global tier (see Asset tiers), not per project. Anything you
add per project under main/.agents/ is projected into main/.claude/,
which is generated — gitignore it. Then cd <root>/main and create a feature:
pm feat new login # bare feature, no agents
pm feat new login --context "Implement login per #42" # solo developer agent
pm feat new login --workflow implement-and-review --context "Implement login per #42"
pm feat new child --base parent # stack on another feature
# Long brief via stdin (--context -), no approval prompt:
pm feat new login --workflow implement-and-review --context - <<'EOF'
Implement the login page.
- validate the email field
- add an integration test
EOFpm feat new creates the branch, worktree, and tmux session (myapp/login).
--workflow <name> picks the agent team to spawn and who to brief; with
--context but no --workflow, pm defaults to the single-agent solo
workflow. See pm feat new --help for stacking, naming, and editor options.
A feature is a branch + worktree + tmux session, tracked in .pm/. Omit
--base and the base is detected from your CWD, so pm feat new child inside
a feature worktree stacks on it (stacked features merge into their parent, not
main).
The lifecycle: pm feat new → work → optionally pm feat pr create /
pm feat ready / pm feat review → pm feat merge (cleans up by default).
Inspection and housekeeping subcommands (list, info, switch, rename,
delete, sync) round out pm feat — see pm feat --help.
Each project is bootstrapped with lifecycle hooks under .pm/hooks/:
post-create.sh (after pm feat new), post-merge.sh (after pm feat merge),
and an opt-in restore.sh (when pm open recreates a session). They run
asynchronously in a dedicated hook tmux window. Edit them to install deps,
run migrations, reopen an editor, etc.; remove a script to disable it.
Two decoupled layers:
- Agent definitions (
~/.agents/agents/<name>.md, ormain/.agents/agents/for one project) describe an agent's job — what it does, how it evaluates work. They carry no routing. pm projects them into each harness's own dir (~/.claude/agents/,main/.claude/agents/for Claude Code) oninit/upgrade; only the.agents/copy counts as a definition. - Workflows (
<pm config dir>/workflows/<name>/, or<project>/.pm/workflows/for one project) define the per-feature topology — who hands off to whom, who reports to the user.
This lets the same implementer play different routing roles in different
features without forking its definition. Every agent ships with the
pm-workflow skill and runs pm workflow show at the start of each task to
discover its routing. pm workflow list shows installed workflows and the
tier each came from.
Bundled agents:
| Agent | Job |
|---|---|
| implementer | Drains its inbox, implements each message, runs tests, addresses reviewer feedback |
| reviewer | Diffs the branch against base, evaluates quality/correctness, sends feedback |
| researcher | Read-only; explores the problem space and sends a refined brief to the implementer |
The definition name default is reserved: it always means a
definition-less vanilla agent session (no definition passed to the
harness), even if a default.md definition file exists. The bundled solo
workflow's team is exactly this name. Earlier releases spelled it claude;
that alias is gone — pm upgrade rewrites solo to name default, and
pm doctor flags any still-running agent named claude (it cannot be
restarted; stop it and respawn).
Bundled workflows:
| Workflow | Routing |
|---|---|
| solo | Single developer owns the feature end-to-end (default when --context is given without --workflow) |
| implement-and-review | Implementer drains tasks; reviewer ↔ implementer loop |
| research-implement-review | Researcher → implementer → reviewer |
| research-only | Researcher explores and reports findings to the user |
| pr-review | Reviewer reviews a checked-out PR and reports to the user (used by pm feat review) |
Each workflow directory holds a config.toml (description, optional
when_to_use hint, agents = the full team spawned at feat new time,
brief_agents = the subset that receives the --context brief) and a
workflow.md (free-form routing prose, with ## <agent> sections; names the
summary.md owner). The bundled workflow names are pm-owned: like agents,
skills, and the baseline they are overwritten by pm upgrade. Directories
under .pm/workflows/ with other names are yours and are never touched;
pm workflow list tags each entry [bundled] or [user].
"Reports to the user" means in the agent's own tmux session, where you read
it live — not by messaging the main orchestrator. main is a dispatcher, not
a relay: it spins up features and steps back, re-engaging only to triage a
feature's summary.md on cleanup. Intra-feature handoffs (reviewer ↔
implementer, researcher → implementer) are what use messaging.
Agent defs carry no tools: allowlist — each inherits the harness's full
tool set (including skills). Real guardrails belong in the permissions layer
(see below), not a per-agent tool list.
Manage agents with pm agent spawn|list|stop|restart|delete|fork. spawn <name> --agent <def> decouples the display/messaging identity from the agent
definition, so you can run several agents off one definition (e.g.
frontend-dev and backend-dev both --agent implementer). fork starts a
new agent from a copy of another's history. See pm agent --help.
Settings live in <project>/.pm/config.toml, or config.toml in the pm
config dir (~/.config/pm/ on Linux, ~/Library/Application Support/pm/ on
macOS) to apply across projects. Project
beats global per key; "" masks the tier below, unset means no flag is passed.
[agents.permissions] # harness's own mode string, passed through unvalidated
implementer = "acceptEdits"
[agents.models] # alias or full id, passed to the harness unvalidated
reviewer = "opus"
[agents.harness] # agent CLI; only "claude-code" (the default) today
implementer = "claude-code"Permission modes and model ids are in the terms of the agent's harness
(--permission-mode / --model values for Claude Code) and reach it
unvalidated, so a typo surfaces in the agent's tmux window rather than at
spawn.
Any other [agents.harness] value is an error at spawn — pm never falls back
silently. pm harness list shows what pm can spawn and pm agent list each
agent's harness; a stored session is only resumed on the harness that
produced it.
pm agent spawn, pm feat new, and pm feat adopt take --permission <mode>
and --model <id> as spawn-time overrides that beat both tiers; on feat new/feat adopt they apply to every agent the workflow spawns. Neither is
remembered — a restart, fork, or heal goes back to config.
Keys are the --agent definition, not the display name: an agent spawned as
frontend-dev --agent implementer takes implementer's row.
pm init installs a Claude Code Stop hook into
main/.claude/settings.json. After every turn it blocks until the agent has
unread messages (calling pm msg wait internally), then returns a block
decision that Claude Code delivers as a continuation prompt. The agent reads
the message, processes it, the turn ends, and the hook fires again. This turns
every pm-managed agent into a never-idle processor: --context at feature
creation just queues the first message, delivered exactly like any later peer
message.
Exception: if a background task or session cron is still running and no messages are queued, the hook lets the turn end so the work isn't stalled.
Reinstall with pm harness hooks install (idempotent, append-only); pm doctor --fix restores a missing one. The installed commands are pm harness hooks stop|session-start; entries written by earlier releases as pm claude hooks … are still recognised and rewritten in place on upgrade.
Agents communicate through a file-based queue, one inbox per agent scoped to
the feature. Each inbox holds an ordered queue per sender with a cursor
tracking the last message processed; pm msg read returns the next unread and
advances the cursor.
pm msg send reviewer "ready for review"
pm msg send reviewer <<'EOF' # multi-line / markdown body via heredoc
## Review findings
Details here.
EOF
pm msg send impl@main "note" # cross-scope: agent in another scope
pm msg read # next unread (auto-picks sender if unambiguous)
pm msg reply "short reply" # reply to the last-read cross-scope message
pm msg wait # block until a new message arrives
pm msg list # enumerate inbox with cursor markersConventions worth knowing (the rest is in pm msg --help):
- Use a quoted-delimiter heredoc (
<<'EOF' … EOF) for any body with markdown, backticks,$, or apostrophes — it's passed verbatim. Reserve the positional"…"form for trivial one-liners. readreads and advances.--index <n>(requires--from) re-reads a past message without moving the cursor; history stays on disk forever.--fromis needed only when ambiguous — if only one sender has unread, it's auto-selected.
Identity resolves as PM_AGENT_NAME (set by pm agent spawn) > $USER >
"user", so spawned agents need no --as-agent.
Each project has an information store at .pm/docs/ for project-level
persistent knowledge — todos, issues, ideas, findings (the default categories,
defined in categories.toml; add your own). The main orchestrator manages it
directly, keeping it lean: completed items are deleted (git history is the
record), with durable learnings migrated into findings.md first.
This is distinct from messaging: the store is a database for durable knowledge, the queue is for cross-agent/cross-scope communication. Don't conflate them.
On pm feat delete/merge, a feature's summary.md is collected to
.pm/summaries/<feature>.md so the orchestrator can triage it into the store.
Bundled assets — skills, agent definitions, workflows, and the shared
baseline — install once per machine and are refreshed by pm upgrade /
pm self-update:
| Tier | Skills / agents / baseline | Workflows |
|---|---|---|
| Global (pm's, plus your machine-wide customs) | ~/.agents/{skills,agents}, ~/.agents/pm-baseline.md |
<pm config dir>/workflows/ |
| Project (your customs only) | main/.agents/{skills,agents} |
<project>/.pm/workflows/ |
Everything resolves project tier first, then global, by name: a project
file with a bundled name overrides it for that project. Bundled names are
pm's in the global tier — pm upgrade rewrites them there — so keep global
customs under names of your own. To customise, copy the bundled file and
edit:
cp ~/.agents/agents/reviewer.md <project>/main/.agents/agents/reviewer.md
pm upgrade # projects it for the harnessFor workflows, copy <pm config dir>/workflows/<name>/ into
<project>/.pm/workflows/<name>/. The pm config dir is ~/.config/pm/ on
Linux and ~/Library/Application Support/pm/ on macOS.
Skills are the exception. Claude Code ranks personal skills above
project ones, the inverse of its agent-definition precedence, and pm
projects every bundled skill into ~/.claude/skills/ — so a project copy
under a bundled skill's name never applies. Customise a bundled skill
globally (edit ~/.agents/skills/<name>/, accepting that pm upgrade
rewrites it) or copy it to a name of your own. pm doctor flags a project
skill shadowed this way.
Upgrading an existing project removes the per-project copies of bundled
assets that earlier releases installed — your own files are never touched.
The copies under .pm/workflows/ are recoverable from .pm/ git history
(commit the deletion with pm state push); the rest lived in generated,
gitignored directories.
Cross-cutting operating rules common to every agent — prose (brevity, no
mannered flourish), the comment/docs and test doctrine, the environment/CWD
conventions, the messaging heredoc form, the pm workflow show reminder,
surfacing out-of-scope problems, what "the user" means — live in a single
bundled pm-baseline.md rather than being repeated per agent.
pm init/pm upgrade install it to ~/.agents/pm-baseline.md, and every
agent pm spawns
(including main) has it appended to its system prompt
(--append-system-prompt-file on Claude Code).
Standing directives you want every spawned agent to obey, composed onto the baseline at spawn time. Two hand-edited markdown files, no command — write or remove notices by editing them directly:
notices.mdin the pm config dir — global, applies in every project.pm/notices.md— per-project
Keep them terse: every line is seeded into every agent on every spawn. Absent
or empty files seed nothing. Both live in the git-backed state repos, so they
sync via pm state push (--global for the global one). Example global
notice:
Hit a pm bug or quirk? Message pm's main agent briefly —
`pm msg send main --project pm '<what broke>'`. Don't try to fix pm from here..pm/ holds all project state (features, agents, messages, config, summaries,
docs) and the pm config dir holds project entries, cross-project config, and
the global workflow tier. Both can be git-backed:
pm state init --remote <url> # init .pm/ repo, set remote, pull
pm state push # auto-commit and push
pm state init --global --remote <url> # same for the global registry
pm state backfill # record repo_url / state_remote for existing projectsThis enables full machine migration — back both up to git, then on a fresh machine:
pm state init --global --remote <global-registry-url>
pm restore # clone repos, pull state, recreate worktrees + sessionsSee pm state --help and pm restore --help.
These round out the tool; each has its full flag reference under --help:
pm open/pm close— recreate or tear down a project's tmux sessions without touching state (e.g. after a reboot).pm openalso runspm doctor's checks and warns about unfixable drift.pm status/pm doctor— project dashboard; audit and auto-fix drift between pm state and git/tmux/GitHub reality.pm harness— the agent harness:hooks, bundledskills/agents(installed to~/.agents/, projected per harness), per-featuresettings, andmigrate|export|importof session data across worktrees and machines (--harness, defaultclaude-code);listthe supported harnesses andprobethe installed binary.pm claude …remains as a hidden alias for one release.pm upgrade/pm self-update— update bundled assets and the binary.pm completions <shell>— generate shell completion scripts.pm list— list registered projects.pm delete— full project teardown (sessions,.pm/, registry entry). Destructive — distinct frompm close, which only tears down sessions.
cargo build
cargo test
cargo clippy
cargo fmtTests spawn real tmux sessions. cargo test runs are capped at 4 threads via
.cargo/config.toml to keep pty usage well under macOS limits. To clean up
stale test servers: for s in /tmp/tmux-$(id -u)/pm-test-*; do tmux -L $(basename "$s") kill-server 2>/dev/null; rm -f "$s"; done.
See AGENTS.md for architecture and development guidelines.