Skip to content

Doctor

polyrepo doctor — a health check in seven sections, mostly read-only diagnosis, plus a few small, non-destructive self-repairs.

bash
polyrepo doctor [options]

Worth running first if any other command is behaving unexpectedly, any time you rename a default branch on GitHub, or just periodically to catch accumulated cruft before it piles up.

Example output — a real run, on a folder of repos with nothing seriously wrong, just the two things doctor actually exists to catch:

Environment

  ✓ Node.js v22.23.2 (>= 20 required).
  ✓ git version 2.49.0.windows.1.
  ✓ gh version 2.69.0 (2025-03-19) — authenticated as macrulezru.
  ! npm 10.9.8 — not authenticated (only needed for `polyrepo publish`). Run `npm login`.

Config

Config file: C:\work\NPM\polyrepo.config.json
Checking 18 package(s)...
  ✓ 18 package(s) discovered.

Remote sync

Checking 18 package(s) against GitHub, and pruning stale remote-tracking refs...
  ✓ 18 package(s) checked — local default-branch cache matches GitHub.
  ✓ os-detect: pruned 2 stale remote-tracking ref(s) (add-more-examples, new-documentation-refactor).

Branch sync

Fetching and comparing 18 package(s) against origin...
  ✓ 18 package(s) checked — all in sync with origin.

Branch protection

Checking 18 package(s) for branch protection...
  ✓ 18 package(s) checked — default branch is protected on all of them.

Stale bump branches

Checking 18 package(s) for leftover bump branches with a merged PR...
  ✓ No stale bump branches found.

Cross-package dependencies

  ✓ No stale local dependency references found.

npm not being logged in is only a warning (publish is the only command that needs it); the two pruned branches on os-detect are refs for PRs merged and deleted on GitHub a while back — doctor is what actually notices and clears them, nothing else in the CLI does.

Sections

  1. Environment — Node.js version (20+ required), whether git/gh/npm are on PATH, and whether gh/npm are authenticated (npm auth is only a warning — it's only needed for publish).

  2. Config — how many packages the current config actually resolves to, and which repos are dirty, in a detached HEAD state, or off their default branch.

  3. Remote sync — two related repairs, both per-repo pointer refreshes that never touch a file, branch, or commit:

    • compares each repo's locally cached default-branch name against what GitHub actually reports right now. Git never refreshes that local cache on its own, so renaming a repo's default branch on GitHub after it was cloned would otherwise go unnoticed by every other command forever — wherever it's drifted, this fixes it with git remote set-head origin --auto;
    • runs git remote prune origin on every repo, dropping local remotes/origin/x refs left over for branches already deleted on GitHub.

    Both are skipped for a repo GitHub can't be reached for (offline, or gh not authenticated).

  4. Branch sync — fetches and compares each repo's local default branch against origin/<default>: diverged (both ahead and behind — a fast-forward won't work, needs resolving by hand), behind only (safe to fast-forward with switch-default), or ahead only (local commits not yet pushed).

  5. Branch protection — whether each repo's default branch actually has GitHub branch protection enabled right now. Report-only; enabling protection is a policy decision, not something this fixes on your behalf.

  6. Stale bump branchesbump merges through a PR with the branch intentionally left on origin, so every completed bump leaves a local branch copy behind too, forever. This reports how many local branches match <version>-version-bump and already have a merged PR. See --clean-branches below.

  7. Cross-package dependencies — does any local package's dependencies/devDependencies/peerDependencies range no longer match another local package's current version.

Options

--clean-branches

Turns section 6 into an interactive checkbox — pick which stale local bump branches to delete. Uses git branch -d, which refuses instead of forcing if a branch somehow isn't actually fully merged locally. The branch on origin is never touched — deleting that is out of scope here, it's more sensitive shared state than a local branch nobody else can see.

Example:

bash
polyrepo doctor --clean-branches