Update Downstream Packages
A release of one of the user's packages just published. Find every
repo of theirs that consumes it, bump the pin (and sibling workspace
packages released together with it), refresh lockfiles, commit, push
to trunk, and verify CI end to end.
Arguments: $ARGUMENTS
This command pushes to trunk of many repos. Nothing is mutated
before the Phase 3 confirmation gate. --no-push runs the whole
procedure but leaves commits local.
Phase 1: Inputs and Pre-flight
- Package — the first non-flag argument. If missing, ask.
- Version — the second non-flag argument, or discover the latest
published version from the package index (for PyPI:
https://pypi.org/pypi/<package>/json; for npm:
npm view <package> version). Confirm the version actually exists
on the index before touching anything — locking against an
unpublished version fails everywhere at once.
- Workspace roots — from
--roots globs. If not given, ask the
user which directories hold their open-source checkouts (e.g.
glob patterns two levels deep under a work directory). Do not
assume a layout.
- Sibling packages — if the package is developed in a workspace
repo that publishes several packages versioned together, list the
siblings (from the source repo's
packages/* manifests or the
index). They bump together.
- Fresh resolver cache — for Python consumers, run
uv cache clean --force once before any locking, so every lock
resolves against the newly published files rather than a stale
cached index.
Phase 2: Discover Consumers
Scan the workspace roots for repos whose manifest depends on the
package:
rg -l '<package>' <root>/*/pyproject.toml <root>/*/package.json
For each hit, record:
- Worktree? If
.git is a file rather than a directory, it is a
linked worktree — skip it; the primary checkout will be found on
its own.
- Trunk —
git symbolic-ref refs/remotes/origin/HEAD, falling
back to whichever of main/master exists.
- Owner — from
git remote get-url origin. Group repos by owner
org; the user confirms which orgs are in scope at the gate.
- Current pin(s) — the pinned version(s) of the package and any
siblings in the manifest.
- Source overrides — dependency sources that shadow the index
(
[tool.uv.sources] entries, npm file:/link:/git: specs).
These repos need the override removed and the published index
package used instead — as its own commit, before the bump.
- Resolver cutoffs — for uv consumers, whether
[tool.uv.exclude-newer] is set and whether the package and every
sibling appear under [tool.uv.exclude-newer-package]. A missing
entry blocks the fresh release at lock time; add the missing
entries as their own commit, before the bump.
- Dirty? Repos with uncommitted changes get reported and skipped
unless the user says otherwise.
Phase 3: Confirmation Gate (mandatory)
Present the plan before mutating anything:
- Per repo: path, owner, branch to update, current pin → new version,
and any prep commits needed (source-override removal,
exclude-newer-package additions).
- Skipped repos with reasons (worktree, dirty, org out of scope).
- Whether pushes will happen (
--no-push inverts the default).
Ask the user to confirm scope via AskUserQuestion — which owner
orgs are in scope, and whether any repo needs a branch override or an
additional branch beyond trunk (some projects maintain a
long-lived second branch that also consumes the package; the user
names it, the same per-repo procedure runs on it). Do not proceed
without confirmation.
Phase 4: Per-Repo Procedure
Repos are independent — parallelize with background agents batched by
owner org (up to 4 batches), or run sequentially if agents are
unavailable. Each repo, on each of its confirmed branches:
Sync — git checkout <branch> && git pull --ff-only.
Prep commits (each on its own, only if discovery flagged it):
- Remove the source override so the repo consumes the published
index package. Commit it alone, e.g.
py(deps[docs]) Use published <package> from PyPI with a
why/what body.
- Add missing entries to
[tool.uv.exclude-newer-package]
(<name> = false, alphabetical order). Commit alone; wrap
exclude-newer-package in backticks in the subject:
py(deps[uv]) Add <name> to `exclude-newer-package` whitelist
with a why/what body noting the cooldown would otherwise block
the fresh release.
Bump pins — replace the pinned version for the package and all
siblings in the manifest. A pattern replacement across the shared
version series catches every sibling at once. With GNU coreutils:
sed -i -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml
On BSD/Darwin, -i requires an explicit backup suffix, and an
empty one edits in place:
sed -i '' -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml
Re-lock — the ecosystem's lock command (uv lock,
npm install --package-lock-only, ...). On a resolution failure,
uv cache clean --force and retry once.
Prerelease warnings mean stop. If locking warns about
prerelease resolution or would need --prerelease flags or
config to accept the version, do not add flags, pins, or config
to force it through — halt this repo and report. A correctly
published release resolves without coaxing; a warning means
something upstream is wrong (stale cache, missing
exclude-newer-package entry, or a bad publish).
Commit — mirror the repo's own previous bump commits for this
package (git log --oneline --grep '<package>'). Fallback
subject, version-arrow form:
py(deps[docs]) <package> <old> → <new>
Body: why/what summarizing what the new release ships — read the
package's CHANGES entry for the version, do not invent.
Push — git push (skip with --no-push). Never force.
Phase 5: Verify with gh
For every pushed repo:
Clear stale Actions caches so CI resolves fresh:
gh cache delete --all --repo <owner>/<repo>
Watch the run for the pushed commit:
gh run list --repo <owner>/<repo> --branch <branch> --limit 1
On failure: clear the cache again and
gh run rerun <run-id> --failed. If it still fails, compare with
the run before the bump — a pre-existing failure is reported as
such, not retried forever.
Where the repo deploys docs from CI, confirm the docs job/workflow
succeeded too — a docs-dependency bump that breaks the docs build
defeats the point.
Output
- Hero line:
✓ <package> <old> → <new> across N repos (or ⚠ with
the failure count).
- Rollout summary — per repo: branch, prep commits, bump commit,
pushed or local, CI status.
- Stopped repos — any halted on prerelease warnings, dirty
trees, or persistent CI failures, each with what the user should
look at.
- Next-step
AskUserQuestion panel: rerun failed CI checks / retry
stopped repos / push the --no-push commits / done.
1---2name: update-downstream-packages3description: Use when rolling a newly published package release through every maintained consumer repository and verifying CI.4---567# Update Downstream Packages89A release of one of the user's packages just published. Find every10repo of theirs that consumes it, bump the pin (and sibling workspace11packages released together with it), refresh lockfiles, commit, push12to trunk, and verify CI end to end.1314Arguments: $ARGUMENTS1516This command **pushes to trunk of many repos**. Nothing is mutated17before the Phase 3 confirmation gate. `--no-push` runs the whole18procedure but leaves commits local.1920---2122## Phase 1: Inputs and Pre-flight23241. **Package** — the first non-flag argument. If missing, ask.252. **Version** — the second non-flag argument, or discover the latest26 published version from the package index (for PyPI:27 `https://pypi.org/pypi/<package>/json`; for npm:28 `npm view <package> version`). Confirm the version actually exists29 on the index before touching anything — locking against an30 unpublished version fails everywhere at once.313. **Workspace roots** — from `--roots` globs. If not given, ask the32 user which directories hold their open-source checkouts (e.g.33 glob patterns two levels deep under a work directory). Do not34 assume a layout.354. **Sibling packages** — if the package is developed in a workspace36 repo that publishes several packages versioned together, list the37 siblings (from the source repo's `packages/*` manifests or the38 index). They bump together.395. **Fresh resolver cache** — for Python consumers, run40 `uv cache clean --force` once before any locking, so every lock41 resolves against the newly published files rather than a stale42 cached index.4344## Phase 2: Discover Consumers4546Scan the workspace roots for repos whose manifest depends on the47package:4849```50rg -l '<package>' <root>/*/pyproject.toml <root>/*/package.json51```5253For each hit, record:5455- **Worktree?** If `.git` is a file rather than a directory, it is a56 linked worktree — skip it; the primary checkout will be found on57 its own.58- **Trunk** — `git symbolic-ref refs/remotes/origin/HEAD`, falling59 back to whichever of `main`/`master` exists.60- **Owner** — from `git remote get-url origin`. Group repos by owner61 org; the user confirms which orgs are in scope at the gate.62- **Current pin(s)** — the pinned version(s) of the package and any63 siblings in the manifest.64- **Source overrides** — dependency sources that shadow the index65 (`[tool.uv.sources]` entries, npm `file:`/`link:`/`git:` specs).66 These repos need the override removed and the published index67 package used instead — as its own commit, before the bump.68- **Resolver cutoffs** — for uv consumers, whether69 `[tool.uv.exclude-newer]` is set and whether the package and every70 sibling appear under `[tool.uv.exclude-newer-package]`. A missing71 entry blocks the fresh release at lock time; add the missing72 entries as their own commit, before the bump.73- **Dirty?** Repos with uncommitted changes get reported and skipped74 unless the user says otherwise.7576## Phase 3: Confirmation Gate (mandatory)7778Present the plan before mutating anything:7980- Per repo: path, owner, branch to update, current pin → new version,81 and any prep commits needed (source-override removal,82 `exclude-newer-package` additions).83- Skipped repos with reasons (worktree, dirty, org out of scope).84- Whether pushes will happen (`--no-push` inverts the default).8586Ask the user to confirm scope via `AskUserQuestion` — which owner87orgs are in scope, and whether any repo needs a branch override or an88**additional** branch beyond trunk (some projects maintain a89long-lived second branch that also consumes the package; the user90names it, the same per-repo procedure runs on it). Do not proceed91without confirmation.9293## Phase 4: Per-Repo Procedure9495Repos are independent — parallelize with background agents batched by96owner org (up to 4 batches), or run sequentially if agents are97unavailable. Each repo, on each of its confirmed branches:98991. **Sync** — `git checkout <branch> && git pull --ff-only`.1002. **Prep commits** (each on its own, only if discovery flagged it):101 - Remove the source override so the repo consumes the published102 index package. Commit it alone, e.g.103 `py(deps[docs]) Use published <package> from PyPI` with a104 why/what body.105 - Add missing entries to `[tool.uv.exclude-newer-package]`106 (`<name> = false`, alphabetical order). Commit alone; wrap107 `exclude-newer-package` in backticks in the subject:108 ``py(deps[uv]) Add <name> to `exclude-newer-package` whitelist``109 with a why/what body noting the cooldown would otherwise block110 the fresh release.1113. **Bump pins** — replace the pinned version for the package and all112 siblings in the manifest. A pattern replacement across the shared113 version series catches every sibling at once. With GNU coreutils:114115 ```116 sed -i -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml117 ```118119 On BSD/Darwin, `-i` requires an explicit backup suffix, and an120 empty one edits in place:121122 ```123 sed -i '' -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml124 ```1251264. **Re-lock** — the ecosystem's lock command (`uv lock`,127 `npm install --package-lock-only`, ...). On a resolution failure,128 `uv cache clean --force` and retry once.1295. **Prerelease warnings mean stop.** If locking warns about130 prerelease resolution or would need `--prerelease` flags or131 config to accept the version, do not add flags, pins, or config132 to force it through — halt this repo and report. A correctly133 published release resolves without coaxing; a warning means134 something upstream is wrong (stale cache, missing135 `exclude-newer-package` entry, or a bad publish).1366. **Commit** — mirror the repo's own previous bump commits for this137 package (`git log --oneline --grep '<package>'`). Fallback138 subject, version-arrow form:139140 ```141 py(deps[docs]) <package> <old> → <new>142 ```143144 Body: why/what summarizing what the new release ships — read the145 package's CHANGES entry for the version, do not invent.1467. **Push** — `git push` (skip with `--no-push`). Never force.147148## Phase 5: Verify with gh149150For every pushed repo:1511521. Clear stale Actions caches so CI resolves fresh:153154 ```155 gh cache delete --all --repo <owner>/<repo>156 ```1571582. Watch the run for the pushed commit:159160 ```161 gh run list --repo <owner>/<repo> --branch <branch> --limit 1162 ```1631643. On failure: clear the cache again and165 `gh run rerun <run-id> --failed`. If it still fails, compare with166 the run before the bump — a pre-existing failure is reported as167 such, not retried forever.1684. Where the repo deploys docs from CI, confirm the docs job/workflow169 succeeded too — a docs-dependency bump that breaks the docs build170 defeats the point.171172## Output1731741. Hero line: `✓ <package> <old> → <new> across N repos` (or `⚠` with175 the failure count).1762. **Rollout summary** — per repo: branch, prep commits, bump commit,177 pushed or local, CI status.1783. **Stopped repos** — any halted on prerelease warnings, dirty179 trees, or persistent CI failures, each with what the user should180 look at.1814. Next-step `AskUserQuestion` panel: rerun failed CI checks / retry182 stopped repos / push the `--no-push` commits / done.