Local orchestration runtime for parallel CLI coding agents.
ppg spawns multiple AI coding agents in isolated git worktrees, each in its own tmux pane, and gives you a single control plane to monitor, aggregate, and merge their work.
When you have a large task — adding tests across a codebase, refactoring multiple modules, fixing a batch of issues — you can break it into independent units and run them in parallel. ppg handles the plumbing: worktree creation, agent spawning, tmux session management, status tracking, result collection, and branch merging.
It works with any CLI agent (Claude Code, Codex, custom scripts) and is designed to be driven by a human or by a "conductor" meta-agent.
- Node.js >= 20
- git (with worktree support)
- tmux (
brew install tmux) - macOS (Terminal.app auto-open uses AppleScript; tmux features work anywhere)
npm install -g pure-point-guardRunning ppg init in any project automatically installs the /ppg skill for Claude Code. This gives Claude the ability to orchestrate parallel agents — just type /ppg in any Claude Code session.
The native macOS dashboard app provides a visual interface for monitoring agents and worktrees.
Via CLI:
ppg install-dashboardOr download directly from GitHub Releases — grab the .dmg file.
# Initialize in your project
cd your-project
ppg init
# Spawn an agent
ppg spawn --name fix-auth --prompt "Fix the authentication bug in src/auth.ts"
# Spawn multiple agents in parallel
ppg spawn --name add-tests --prompt "Add unit tests for src/utils/"
ppg spawn --name update-docs --prompt "Update the API documentation"
# Check status
ppg status
# Watch status live
ppg status --watch
# View agent output
ppg logs ag-xxxxxxxx
# Collect results
ppg aggregate --all
# Merge a completed worktree back
ppg merge wt-xxxxxx
# Open the dashboard
ppg uiEach ppg spawn creates a git worktree, opens a tmux pane, launches the agent, and pops open a Terminal.app window so you can watch it work.
your-project/
├── .pg/
│ ├── config.yaml # Agent and project config
│ ├── manifest.json # Runtime state (worktrees, agents, status)
│ ├── templates/ # Reusable prompt templates
│ ├── prompts/ # Reusable prompt files for swarms
│ ├── agent-prompts/ # Rendered per-agent prompt files (ephemeral)
│ └── results/ # Agent result files
├── .worktrees/
│ ├── wt-abc123/ # Isolated git worktree
│ └── wt-def456/ # Another worktree
└── ...
Worktrees are isolated git checkouts on their own branches (ppg/<name>). Each agent works in its own worktree so there are no file conflicts between parallel agents.
tmux provides the process management layer. One session per project, one window per worktree, one pane per agent. This gives you ppg logs, ppg status, ppg attach, and ppg kill for free.
Terminal.app windows open automatically when agents spawn (macOS), so you can see what every agent is doing without manually attaching. Use --no-open to suppress this.
Initialize ppg in the current git repository. Creates .pg/ directory with default config and a sample template. Also installs the /ppg Claude Code skill if Claude Code is available.
ppg init
ppg init --jsonSpawn a new worktree with agent(s), or add agents to an existing worktree.
# Inline prompt
ppg spawn --name fix-bug --prompt "Fix the null check in parser.ts"
# From a file
ppg spawn --name refactor --prompt-file tasks/refactor-auth.md
# Using a template with variables
ppg spawn --name add-tests --template test-writer --var SCOPE=auth --var STYLE=unit
# Multiple agents in one worktree
ppg spawn --name big-task --prompt "Implement feature X" --count 2
# Split panes in one window
ppg spawn --name big-task --prompt "Implement feature X" --count 2 --split
# Add agent to existing worktree
ppg spawn --worktree wt-abc123 --prompt "Review the changes"
# Silent mode (no Terminal window)
ppg spawn --name ci-task --prompt "Run linting" --no-open
# Specify base branch
ppg spawn --name hotfix --prompt "Fix critical bug" --base release/v2| Option | Description |
|---|---|
-n, --name <name> |
Name for the worktree (becomes branch ppg/<name>) |
-a, --agent <type> |
Agent type from config (default: claude) |
-p, --prompt <text> |
Inline prompt text |
-f, --prompt-file <path> |
Read prompt from file |
-t, --template <name> |
Use a template from .pg/templates/ |
--var <key=value> |
Template variable (repeatable) |
-b, --base <branch> |
Base branch for the worktree |
-w, --worktree <id> |
Add agent to existing worktree instead of creating new |
-c, --count <n> |
Number of agents to spawn (default: 1) |
--split |
Put all agents in one window as split panes |
--no-open |
Don't open a Terminal window |
--json |
JSON output |
Show status of all worktrees and agents.
ppg status # Pretty table
ppg status --json # Machine-readable
ppg status --watch # Live updates
ppg status my-task # Filter by worktree nameOpen a terminal attached to a worktree or agent pane. If you're already in tmux, it switches to the target pane. Otherwise it opens a new Terminal.app window.
ppg attach wt-abc123 # By worktree ID
ppg attach my-task # By worktree name
ppg attach ag-xxxxxxxx # By agent IDView captured output from an agent's tmux pane.
ppg logs ag-xxxxxxxx # Last 100 lines
ppg logs ag-xxxxxxxx --lines 500 # Last 500 lines
ppg logs ag-xxxxxxxx --follow # Tail (polls every 1s)
ppg logs ag-xxxxxxxx --full # Full pane historyKill agents and optionally remove worktrees.
ppg kill --agent ag-xxxxxxxx # Kill one agent
ppg kill --worktree wt-abc123 # Kill all agents in worktree
ppg kill --all # Kill everything
ppg kill --all --remove # Kill everything and remove worktreesCollect result files from completed agents.
ppg aggregate wt-abc123 # Results from one worktree
ppg aggregate --all # Results from all worktrees
ppg aggregate --all -o results.md # Write to fileMerge a worktree branch back into its base branch.
ppg merge wt-abc123 # Squash merge (default)
ppg merge wt-abc123 -s no-ff # No-ff merge
ppg merge wt-abc123 --dry-run # Preview
ppg merge wt-abc123 --no-cleanup # Keep worktree after merge
ppg merge wt-abc123 --force # Merge even if agents aren't doneShow changes made in a worktree branch relative to its base.
ppg diff wt-abc123 # Full diff
ppg diff wt-abc123 --stat # Diffstat summary
ppg diff wt-abc123 --name-only # Changed file names onlyRestart a failed or killed agent in the same worktree.
ppg restart ag-xxxxxxxx # Restart with original prompt
ppg restart ag-xxxxxxxx --prompt "Try again" # Override the prompt
ppg restart ag-xxxxxxxx --agent codex # Override the agent typeSend text or keystrokes to an agent's tmux pane.
ppg send ag-xxxxxxxx "yes" # Send text + Enter
ppg send ag-xxxxxxxx "y" --no-enter # Send text without Enter
ppg send ag-xxxxxxxx "C-c" --keys # Send raw tmux key namesWait for agents to reach a terminal state (completed, failed, killed, lost).
ppg wait wt-abc123 # Wait for all agents in worktree
ppg wait --all # Wait for all agents everywhere
ppg wait --all --timeout 300 # Timeout after 5 minutes
ppg wait --all --interval 10 # Poll every 10 secondsRemove worktrees in terminal states (merged, cleaned, failed).
ppg clean # Clean merged/cleaned worktrees
ppg clean --all # Also clean failed worktrees
ppg clean --dry-run # Preview what would be cleaned
ppg clean --prune # Also run git worktree pruneCreate a standalone worktree without spawning any agents.
ppg worktree create --name my-branch
ppg worktree create --name my-branch --base developList available prompt templates from .pg/templates/.
Download and install the native macOS dashboard app from GitHub Releases.
ppg install-dashboard # Install to /Applications
ppg install-dashboard --dir ~/Apps # Custom install directoryOpen the native macOS dashboard app (alias: ppg dashboard).
.pg/config.yaml:
sessionName: ppg
defaultAgent: claude
agents:
claude:
name: claude
command: claude --dangerously-skip-permissions
interactive: true
resultInstructions: >-
When you have completed the task, write a summary of what you did
and any important notes to the file at: {{RESULT_FILE}}
worktreeBase: .worktrees
templateDir: .pg/templates
resultDir: .pg/results
logDir: .pg/logs
envFiles:
- .env
- .env.local
symlinkNodeModules: trueAdd any CLI agent by defining it in the agents map:
agents:
claude:
name: claude
command: claude --dangerously-skip-permissions
interactive: true
codex:
name: codex
command: codex
promptFlag: --prompt
interactive: true
custom-script:
name: custom
command: ./scripts/my-agent.sh
promptFlag: --task
interactive: falseThen spawn with ppg spawn --agent codex --prompt "...".
Templates live in .pg/templates/ as Markdown files with {{VAR}} placeholders.
Built-in variables:
| Variable | Value |
|---|---|
{{WORKTREE_PATH}} |
Path to the agent's worktree |
{{BRANCH}} |
Git branch name |
{{AGENT_ID}} |
Agent identifier |
{{RESULT_FILE}} |
Where the agent should write results |
{{PROJECT_ROOT}} |
Repository root path |
{{TASK_NAME}} |
Worktree name |
{{PROMPT}} |
The prompt text |
Custom variables are passed with --var KEY=VALUE.
Example template (.pg/templates/test-writer.md):
# Task: {{TASK_NAME}}
Write comprehensive tests for the {{SCOPE}} module.
## Working Directory
{{WORKTREE_PATH}}
## Guidelines
- Use {{FRAMEWORK}} for test framework
- Aim for >90% coverage
- Write your summary to {{RESULT_FILE}} when doneppg spawn --name auth-tests --template test-writer --var SCOPE=auth --var FRAMEWORK=vitestspawning → running → completed (result file written)
→ failed (non-zero exit or shell prompt visible)
→ killed (via ppg kill)
→ lost (tmux pane died unexpectedly)
Status is determined by checking (in order):
- Result file exists →
completed - Tmux pane gone →
lost - Pane dead with exit 0 →
completed - Pane dead with non-zero exit →
failed - Pane alive, shell prompt visible →
failed(agent exited without writing results) - Otherwise →
running
ppg is designed to be driven programmatically by a meta-agent (a "conductor"). All orchestration commands (spawn, status, kill, wait, aggregate, merge) support --json for machine-readable output.
Conductor workflow:
# 1. Plan — break the task into independent units
# 2. Spawn agents
ppg spawn --name task-1 --prompt "Do X" --json
ppg spawn --name task-2 --prompt "Do Y" --json
# 3. Poll for completion
ppg status --json # check for status: "completed" or "failed"
# 4. Wait for all agents
ppg wait --all --json
# 5. Aggregate results
ppg aggregate --all --json
# 6. Merge completed work
ppg merge wt-xxxxxx --jsonKey principles:
- Always use
--jsonfor machine-readable output - Poll status every 5 seconds or use
ppg wait - One concern per worktree for clean merges
- Use
ppg aggregateto collect and review results before merging
src/
├── cli.ts # Entry point — registers commands with Commander.js
├── commands/ # Command implementations
├── core/ # Domain logic (manifest, agent, worktree, tmux, terminal, config)
├── lib/ # Utilities (paths, errors, id, output, shell)
└── types/ # Type definitions
Built with TypeScript (strict, ES2022, ESM-only), Commander.js for CLI framework, and tmux + git worktrees as foundational abstractions. See CONTRIBUTING.md for development details.
See CONTRIBUTING.md for development setup, testing, and code conventions.