Puppet Master

Puppet Master

10 min read Original article ↗
Puppet Master

All your agents in one place.
At your desk and on your phone.

Puppet Master spawns and supervises real Claude Code, Codex, Gemini and more sessions across every project you have open. It shows what each one is doing, and tells you the moment one is waiting on you.

InstallmacOS and Linux · v0.10.0

$curl -fsSL https://dl.puppet-master.xyz/install.sh | sh

thenpm daemon›dashboard on http://127.0.0.1:7676/

v0.10.0MITmacOS and Linux5 agent CLIs

A multiplexer for agent sessions

Every session in the dashboard is a live PTY rendered with xterm.js — the same terminal the agent is driving. You can type into it, resize it and scroll back through it, and have it open in the browser, on the iOS app, in pm attach and in the TUI at the same time.

Puppet Master starts the agent CLI you already use and watches it, rather than reimplementing it on top of an API. Nothing about how you work with an agent changes — the prompts, the permission requests and the output are the ones you would get in your own terminal.

Running several agents that way means several terminal windows and a mental map you maintain yourself: where each one lives, what it is doing, how far along it is, and which one is sitting blocked. That map stops scaling at a handful of sessions, and the cost of dropping one is an agent that finished an hour ago, or went the wrong way, without you knowing.

So the daemon owns the PTYs. Coarse state — working, needs input, idle, done, errored — comes from watching the process. Activity and progress are reported by the agent itself through an MCP channel wired into every session it spawns, so the list is not guessing from scraped terminal output.

Which means you can work the way you already do: one agent in front of you, the others carrying on, and a list that tells you which one to look at next.

One session flagged needs-input with the question it is blocked on legible, and the answer box in reach.

Where the work happens

Agents run on workers

A worker is a machine that runs the sessions the controller sends it. It is the unit of placement, and how coarse or fine you make it is yours to choose: a dozen agents sharing one worker, or a worker each on machines across the building.

  • One worker runs many sessions. The simplest arrangement is the default one: the controller’s own machine is a worker, and everything runs there. Nothing to enroll and nothing to configure.
  • One machine can be several workers. pm worker --name buildenrolls under that name, and each name is a separate worker to the controller. Give a noisy job its own and it cannot disturb the others.
  • Workers can be anywhere. A spare box, a VM, a machine that happens to have the GPU or the license or the network route. One enrollment command and it starts taking sessions, with the terminals streaming back exactly as local ones do.
  • Sessions are placed, not scattered. Choose a worker when you spawn, or let a project carry its own path on each worker so the same project resolves wherever it runs.

Running several is the ordinary case rather than an advanced one. pm workeron each machine you want to use, or several times on one machine under different names, and the controller spreads sessions across whatever is online.

Manage → Hosts with more than one sandbox enrolled, each row carrying its online badge, name, platform, which end dialed, and the pm build it is running. The Hosts page shows no container runtime, so nothing here claims one. The point to make visible is that the work is not on the controller's machine, and that one machine can appear as several hosts.

A worker in a container, sharing a folder

The arrangement worth setting up first: the controller on your own machine and the worker in something disposable, with your project mounted into it.

$ pm worker --sandbox --dir ~/src/acme

That starts a container, runs an ordinary worker inside it, enrolls it, and mounts ~/src/acme at the same absolute path inside. The agent gets a machine it can wreck and you keep yours, with your editor still looking at the same files.

  • Filesystem isolation. With --sandbox, agents on the worker can access only the host directories you share with --dir. Repeat --dir to share multiple directories.
  • Incus, Docker or Podman, whichever it finds. On Linux it prefers an Incus system container, where an agent can run Docker itself. Cap what it may use with --cpu and --memory.

Code review

Ask for a review, and the agent waits on you

You do not open a review. You ask the agent that did the work for one, and it opens it, hands you a URL, and then stops and waits for your first comment.

get me a review open for what you just did

The ask and its answer in one frame: the message to the agent in its session — something like “get me a review open for what you just did” — and the review appearing, either as the URL coming back or as the review tab opening beside the session. This is the picture the page is built around, because it is the thing people describe when they describe this feature.
  • The agent answers as you go. It stays on the review while it is open and responds to each comment as you leave it, editing the files as it goes.
  • The diff updates underneath you. A review reads the working tree, so the edits the agent makes answering your comment appear in the same diff you are reading. It is a live document, not a snapshot of a branch.
  • Threads follow the code. A comment comes back with its current line, so it keeps pointing at the right place as the file changes.
  • It does not have to be code. Point a review at a single document and it is read against an empty baseline, with no repository involved — a design note or a draft, reviewed the same way.

You read and answer a review in the dashboard. The guide covers what the agent does on the other side of it.

A thread on a line of the diff: the comment, and the agent's answer under it. The point is that a review is a conversation and that it happens in the diff rather than beside it.

Connections

Give them agency, securely.

A connection is an MCP server or a REST API that a project's agents may use through the controller. The agent drafts it from its session, you finish it in the dashboard, and from then on reads run while writes wait for you.

  • The agent drafts, you finish. It seeds the connection with the endpoint and the API document and the setup opens beside its session. The credentials go into that panel, in the dashboard, and never into the conversation.
  • Reads run, writes wait. Each tool is classified as a read or a write, with a default policy for each and an override per tool. A call the policy holds appears as an approval, with the agent's justification and the exact arguments it will send, and it runs only when you tap.
  • The keys stay on the controller. The controller makes every downstream request. The agent sees that credentials are stored and nothing else, so a prompt that goes wrong cannot read them out.
  • Every call is on the record. Who asked, which tool, the exact arguments, who approved, and the result, kept on the connection's own page and open at a link you can share.

An MCP server over HTTP, or any OpenAPI 3 document, where each operation becomes a tool. The guide walks through one from the agent's request to the approved call.

A real Claude Code session asked to set up a connection to an API: the request, the agent reading the OpenAPI document and calling seed_connection, and its reply saying the draft is open beside the session, with the prompt line at the bottom in view.

Notified, then answered

Answer from your phone

The iOS app is the same fleet on a smaller screen: the session list with the ones that need you at the top, the real terminal to type into, and a planning view for answering a decision an agent is waiting on.

  • It is the terminal, again. The same live session, attachable from the phone, scrollback and all — not a read-only view of what happened.
  • A notification you can act on. A session that needs input reaches you wherever you are. Tap it and you land on that session, ready to answer. The payload is sealed on your controller and opened on the device, so the relay that carries it cannot read it, and every one is padded to a fixed length so its size says nothing either.
  • Answer a decision properly. A plan an agent is waiting on opens as its own view — the decision, the conversation, and the full plan — so the reply you give from a phone is the same reply you would give at the desk.
  • The phone gets its own credential. Enroll it with your password once and it holds a revocable device credential rather than your password. Revoke it from the dashboard and that device is out.

What you get

Ten things the dashboard does that a wall of terminal windows does not.

01

Every session, one list

Buckets, projects and sessions in a single sidebar, with the ones that need you pinned to the top. Each row carries its state, the agent’s own activity line, and up to three chips the agent chose.

You work in one session at a time and switch from the sidebar. The list is how the rest stay in view without being on screen, so nothing is lost by not looking at it.

02

One session, several viewers

The same terminal open in the browser, in pm attach and in the TUI at once, all live, with scrollback replayed to each new viewer.

A session owns one agent terminal and as many shells as you open beside it. Pane identity survives an agent resume and a shell restart.

03

It tells you when you are needed

Lifecycle hooks injected into the agent's own session flip it to a loud needs-input state the moment it asks for a permission, rather than guessing from scraped output. pm attach rings the terminal bell.

Answer from the dashboard, the TUI or the phone, and the agent carries on.

04

Sessions outlive the shell

The daemon holds the PTYs and persists to SQLite, so you can detach, close the laptop, restart the dashboard, and attach again from somewhere else with the scrollback intact.

Closing a session’s tab in the browser does not stop the session.

05

Instruction overlays

Instruction overlays attach to a bucket or a project and are compiled into every session spawned under them, so a convention you want followed does not have to be retyped into each agent.

They are versioned, so you can see what a session was told at the time it ran rather than what the overlay says now.

06

Issue tracking

Every bucket has a board of cross-channel work — an email awaiting a reply, a pull request to review, a ticket — that sessions keep current through the daemon’s MCP tools.

Spawn from an item and it flips to in progress with the session linked, and every change lands on an append-only history.

07

Plan interface

An agent keeps a plan in the project tree, synchronizes it over MCP, and presents one decision at a time in its own session tab, rather than handing you a document to approve whole.

Options may be single, multiple, or supplied by you, with per-option notes and a dialogue that stays open while a submitted decision waits for the agent.

08

Share artifacts and ports

An agent that starts a dev server publishes it and gets back a URL you can open, even when the session runs on another machine. It appears on the session’s pane with a button to close it.

A directory of output works the same way: the worker serves it, the agent starts nothing, and both survive a restart at the same URL.

09

Connections and approvals

An MCP server or a REST API becomes a connection a project's agents can use. The agent drafts the setup, you enter the credentials in the dashboard, and a policy says which tools run and which wait for your approval.

Every call goes through the controller with credentials the agent never holds, and every one is on the record with its exact arguments and result.

10

Custom models

Model profiles point sessions at a gateway, a self-hosted proxy, or any service that speaks one of the APIs the agents use, instead of the agent CLI’s own account. One endpoint entry per API dialect, so the same profile serves whichever agent runs.

Set one per session, or give a project a default and let every session in it inherit.