pma
Maintain many repositories from one place.
If you keep dozens of small projects, the work is not hard, it is scattered. Each repository has a TODO.md, failing CI, outdated dependencies and a dirty working tree, and finding out means opening all of them. pma reads them all in one pass, ranks every task across every project in one list, and can hand a task to a coding agent in a throwaway worktree. You read the diff, and pma commits it and opens a pull request or pushes.
Scanning 95 repositories takes about 15 seconds.
Nothing is published without you. An agent runs with no push credentials, pma runs the project's tests itself rather than trusting the agent's report, and every change waits in a worktree until you approve it.
Features
One view of everything
-
pma next, orpmaalone: one list of tasks for agents, in the orderpma dispatch --autotakes them, and one of what waits on you: runs to review, publish or merge, then critical or urgent tasks no agent may take. -
Every task in every project in one Eisenhower matrix, ranked by project importance and item priority.
-
Failing CI, outdated dependencies, idle repositories and dirty working trees appear as tasks beside the written ones.
-
A terminal browser (
pma tui) ofnextand the matrix, projects worst first by what needs attention (pma status), and an oldest-first list to prune (pma stale). -
Portfolio notes that belong to no single project.
Tasks in plain Markdown
-
A root
TODO.mdper repository is the only task file. Nothing to keep in step, and no lock-in: removepmaand the files still read. -
pma lintchecks the format.pma pruneremoves what is finished. -
pma syncmirrorsCriticalitems to GitHub Issues and ticks items whose issue was closed.
Agents, with the brakes on
-
claude,opencodeandompcome as templates. Any coding agent with a headless mode is a record you edit, not a code change. -
sandukis the same agent in a disposable container: the key stays on the host, the worktree is mounted at its own path, and the box is the bound. Needs sanduk and a container engine;pma agent show sandukprints what it runs. Verify still runs on the host, so a run that edits the build file is contained and the build is not. -
Each run gets its own worktree of the remote default branch, so your dirty checkout is never touched.
-
The agent runs without push credentials.
pmaruns the project's test command itself, before and after, and reports both ends. -
Every run records the diff, the test result, the cost, the duration and the paths it touched. A run that changed a file its task had no business changing is flagged.
-
Limits you set: agents at once, dollars per run, dollars per batch, minutes per run.
-
pma reportsays what dispatching has produced: first-attempt passes, reworks, merges, cost, review time.
Which model, where
-
A preset names a worker, a model and the arguments that configure it:
pma preset set omp-luna-high omp gpt-5.6-luna --thinking high. -
pmaruns agents and is not an API client. Provider keys stay the agent's, reached through its own configuration, andpmastores none of them. -
A routing policy decides the worker, the model and how much autonomy per kind of task. It is a document you propose, activate, and can replay over past runs to see what it would have changed.
Repeating work
-
A campaign applies one task definition across many repositories, each verified on its own.
-
A workflow is a graph of agents over a project: review, then validate the review, then fix what it confirmed. Written as JSON or as a script, and costed before it runs.
-
pma workflow runprints a plan: each agent node it would run, over which units, with the worker, the model and a ceiling. Nothing is spent until you approve that plan by its id, and an approval runs that plan and nothing else. Its target isdispatch's: a project, aTODO.mdline, a heading or a signal, whichever type the workflow reads. -
One run of a workflow is an instance. It is frozen at the revision it started under, so activating a new one does not change work already under way, and
--instance <id>resumes it where the last pass stopped. A node runs once every node before it has finished, so a join sees every branch. A node that edits a repository leaves a run inpma reviewlike any other. -
Each node can run on its own worker and model: a route that names the node, such as
{"match": {"node": "*/review"}, "model": "haiku"}, picks them.
Install
From a clone: make install, which builds the release binary and copies it to ~/.local/bin. Set PREFIX to install elsewhere: make install PREFIX=/usr/local.
Requirements:
-
macOS or Linux. Rust 1.89 or later, and a C compiler: SQLite is compiled in.
-
gitonPATH. -
A coding agent for
pma dispatch:claude,opencodeoromp, logged in or holding its own provider key. -
gh, authenticated, for CI status, for opening pull requests and forpma sync. Without it CI reads as unknown, andpma scan --offlineskips GitHub on purpose.
TODO.md format
-
-
-
-
The rules, which pma lint checks:
-
The first non-blank line is
# TODO, and there is no second#heading. -
Priority comes from the enclosing
##section:Critical,High,MediumorLow, spelled exactly, at most one of each. Other##sections are allowed and ignored. -
An item is one line,
- [ ] textor- [x] text. No other spelling of the checkbox. -
Indented lines under an item are its description, carried to the agent verbatim.
-
A
###heading groups the items below it, until the next heading. -
Trailing tokens, read from the end of the line:
#tag,due:YYYY-MM-DD,gh:Nfor a linked issue, and an optional id,^k3f9q: five characters of0-9anda-zwithouti,l,ooru. A token-shaped word earlier in the line is text, and so is a caret word that is not an id, such as^1.2. -
#urgentmakes an item urgent whatever its date.#agentmarks one an agent may take.#manualkeeps agents off it entirely. -
A finished item is ticked where it stands.
pma pruneremoves it.
Two open items with the same text, or two items with the same gh:N or id, are errors. An item is identified by its id, else its issue number, else its text. Ids are optional and pma writes them: pma lint --ids --apply gives one to every open item, and an item a workflow adds gets one. With an id, rewording an item keeps its age and any run it has.
Commands
Projects and tasks:
next, matrix, status and tui read the last scan. They do not rescan.
Agents, models and presets:
Highest wins: the -a and -m flags, then -p <preset>, then an applied route, then the default preset.
Dispatch, review, publish:
A target naming one task fails if that task cannot run. A target naming many passes over each with its reason, and dispatches the rest.
Each run gets a worktree of the remote default branch under ~/.local/state/pma/worktrees, on a pma/ branch. The item must be open in the remote TODO.md, so commit and push before dispatching. Set the test command with pma config projects.myproject.verify "make check", or let it be detected.
A run opened with pma pr is pr-open until the pull request is merged or closed; pma review settles it and shows review activity meanwhile. A run published with pma push is pushed.
Repeating work:
A pass runs every node a rule decides, then stops and prints a plan for the agent nodes that are ready: as many runs as batch_budget admits at agent_budget each. --approve <plan> runs exactly that plan, max_parallel agents at a time, then stops at the next plan. A plan that changed since it was printed has another id and is refused. A unit a node could not handle is named on stderr and goes no further than a default edge; a check waiting on a pull request keeps its unit until the next pass.
GitHub Issues:
Each ## Critical item gets an issue labelled pma:critical, and gh:N is written into its line. An item whose issue is closed is ticked. Edits to TODO.md are left uncommitted, for you to read first.
Where things live
-
The database is
~/.config/pma/projects.db, or$PMA_HOME/projects.db. It serves one machine: two copies cannot be merged. -
Worktrees, logs and artifacts sit under
~/.local/state/pma($XDG_STATE_HOME/pma), or$PMA_HOME/statewhenPMA_HOMEis set;PMA_STATEoverrides both. Worktrees are removed when a run is published or rejected. A workflow's agent nodes read the code in a worktree of their own and leave nothing behind; what they read and wrote stays inartifacts/<instance>/<node>/<n>/, numbered per run. -
Settings live in the database rather than a file.
pma configlists them,pma config <key> <value>sets one, and--resetclears one.
One command that runs agents or edits worktrees runs at a time. dispatch, pr, push, workflow run and review --reject take a lock and name the process holding it. Reading commands run alongside.
Development
Licence
MIT.