Reference
Architecture
Every command starts from the same config file (roots/packages/gitlabHosts) resolved into a flat list of repos. Read-only checks (git status, tag/release/registry/PR lookups) run through a small concurrency-limited parallel map, so a full list or doctor across many repos doesn't wait on one repo at a time. Anything that mutates state — a commit, push, merge, or tag — goes through one shared git/npm execution layer (plus gh or glab, per repo) that always prints the command it's about to run and, for mutating steps, respects --dry-run uniformly across every command that has one.
Host-specific logic (PR/MR create & merge, CI-check waiting, release creation, branch protection, default-branch lookup) lives behind one shared interface with two implementations — a GitHub adapter over gh and a GitLab adapter over glab. Every command that needs a repo's host resolves it once, from git remote get-url origin, rather than being told which one to use — see Config & Discovery.
bump is the one command with real cross-run state to track: instead of storing anything itself, it re-derives exactly how far a previous attempt got (branch pushed? PR/MR opened? merged?) straight from git/the host on every run, which is what makes re-running it after an interruption safe without any local bookkeeping to go stale.
A command does not print directly: it reports through a small runtime (headings, steps, status lines, tables, progress, and the questions it asks), and the terminal and the web interface are two implementations of it. The terminal one prints what the commands always printed and asks with prompts. The web one turns the same calls into events that a page renders, and each run goes to its own child process, so two runs, or a run and a cancel, never share state. That is why polyrepo ui can run every command and the output is the same in both places.
Comparison
Narrower in scope than a monorepo build orchestrator (Nx, Turborepo, Lerna's task-running side) — polyrepo-cli doesn't build a dependency graph or cache task output, because it's built for a folder of independent repos, each with its own package.json and its own remote (GitHub or GitLab), not a single workspace. It also doesn't replace a changelog/versioning tool like Changesets — it drafts a CHANGELOG.md entry as part of bump, but the actual workflow it automates is the git/host/npm mechanics around a release (branch, PR/MR, merge, tag, release, npm publish) across many repos at once, not changeset accumulation within one.
Development
git clone https://github.com/macrulezru/polyrepo-cli.git
cd polyrepo-cli
npm install
npm test
npm run dev:ui # the interface with hot reload, against a running `polyrepo ui`
npm run build:ui # builds the interface into ui-dist/Unit tests cover the pure logic that doesn't need a real git/gh/glab/npm — version bumping, CHANGELOG.md entry insertion, cross-package dependency drift detection and range rewriting (for sync-deps), dist-tag resolution (for publish), bump-branch-name pattern matching, and git-host detection from a remote URL — using real temporary files on disk rather than mocks. The web interface has its own tests that start the server on a free port and drive it over HTTP: the access token, the config API, runs and their questions, cancelling, the history and the saved sets.
License
MIT — see LICENSE.