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 ask-user-choice — 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
ask-user-choice panel: rerun failed CI checks / retry
stopped repos / push the --no-push commits / done.
Portability notes
ask-user-choice — present the listed options and wait for the user to pick one. Hosts with a structured multiple-choice tool (Claude Code's AskUserQuestion) should use it; otherwise print a numbered list and wait for a numbered reply. Never proceed on an assumed answer.
$ARGUMENTS — the text the user passed when invoking this skill. If your host does not substitute it, read it as the user's request in the current turn, and ask when there is none.
1---2name: release-update-downstream-packages3description: Use when rolling a newly published package release through every maintained consumer repository and verifying CI.4---56# Update Downstream Packages78A release of one of the user's packages just published. Find every9repo of theirs that consumes it, bump the pin (and sibling workspace10packages released together with it), refresh lockfiles, commit, push11to trunk, and verify CI end to end.1213Arguments: $ARGUMENTS1415This command **pushes to trunk of many repos**. Nothing is mutated16before the Phase 3 confirmation gate. `--no-push` runs the whole17procedure but leaves commits local.1819---2021## Phase 1: Inputs and Pre-flight22231. **Package** — the first non-flag argument. If missing, ask.242. **Version** — the second non-flag argument, or discover the latest25 published version from the package index (for PyPI:26 `https://pypi.org/pypi/<package>/json`; for npm:27 `npm view <package> version`). Confirm the version actually exists28 on the index before touching anything — locking against an29 unpublished version fails everywhere at once.303. **Workspace roots** — from `--roots` globs. If not given, ask the31 user which directories hold their open-source checkouts (e.g.32 glob patterns two levels deep under a work directory). Do not33 assume a layout.344. **Sibling packages** — if the package is developed in a workspace35 repo that publishes several packages versioned together, list the36 siblings (from the source repo's `packages/*` manifests or the37 index). They bump together.385. **Fresh resolver cache** — for Python consumers, run39 `uv cache clean --force` once before any locking, so every lock40 resolves against the newly published files rather than a stale41 cached index.4243## Phase 2: Discover Consumers4445Scan the workspace roots for repos whose manifest depends on the46package:4748```49rg -l '<package>' <root>/*/pyproject.toml <root>/*/package.json50```5152For each hit, record:5354- **Worktree?** If `.git` is a file rather than a directory, it is a55 linked worktree — skip it; the primary checkout will be found on56 its own.57- **Trunk** — `git symbolic-ref refs/remotes/origin/HEAD`, falling58 back to whichever of `main`/`master` exists.59- **Owner** — from `git remote get-url origin`. Group repos by owner60 org; the user confirms which orgs are in scope at the gate.61- **Current pin(s)** — the pinned version(s) of the package and any62 siblings in the manifest.63- **Source overrides** — dependency sources that shadow the index64 (`[tool.uv.sources]` entries, npm `file:`/`link:`/`git:` specs).65 These repos need the override removed and the published index66 package used instead — as its own commit, before the bump.67- **Resolver cutoffs** — for uv consumers, whether68 `[tool.uv.exclude-newer]` is set and whether the package and every69 sibling appear under `[tool.uv.exclude-newer-package]`. A missing70 entry blocks the fresh release at lock time; add the missing71 entries as their own commit, before the bump.72- **Dirty?** Repos with uncommitted changes get reported and skipped73 unless the user says otherwise.7475## Phase 3: Confirmation Gate (mandatory)7677Present the plan before mutating anything:7879- Per repo: path, owner, branch to update, current pin → new version,80 and any prep commits needed (source-override removal,81 `exclude-newer-package` additions).82- Skipped repos with reasons (worktree, dirty, org out of scope).83- Whether pushes will happen (`--no-push` inverts the default).8485Ask the user to confirm scope via `ask-user-choice` — which owner86orgs are in scope, and whether any repo needs a branch override or an87**additional** branch beyond trunk (some projects maintain a88long-lived second branch that also consumes the package; the user89names it, the same per-repo procedure runs on it). Do not proceed90without confirmation.9192## Phase 4: Per-Repo Procedure9394Repos are independent — parallelize with background agents batched by95owner org (up to 4 batches), or run sequentially if agents are96unavailable. Each repo, on each of its confirmed branches:97981. **Sync** — `git checkout <branch> && git pull --ff-only`.992. **Prep commits** (each on its own, only if discovery flagged it):100 - Remove the source override so the repo consumes the published101 index package. Commit it alone, e.g.102 `py(deps[docs]) Use published <package> from PyPI` with a103 why/what body.104 - Add missing entries to `[tool.uv.exclude-newer-package]`105 (`<name> = false`, alphabetical order). Commit alone; wrap106 `exclude-newer-package` in backticks in the subject:107 ``py(deps[uv]) Add <name> to `exclude-newer-package` whitelist``108 with a why/what body noting the cooldown would otherwise block109 the fresh release.1103. **Bump pins** — replace the pinned version for the package and all111 siblings in the manifest. A pattern replacement across the shared112 version series catches every sibling at once. With GNU coreutils:113114 ```115 sed -i -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml116 ```117118 On BSD/Darwin, `-i` requires an explicit backup suffix, and an119 empty one edits in place:120121 ```122 sed -i '' -E 's/==0\.0\.1a[0-9]+/==0.0.1a<N>/g' pyproject.toml123 ```1241254. **Re-lock** — the ecosystem's lock command (`uv lock`,126 `npm install --package-lock-only`, ...). On a resolution failure,127 `uv cache clean --force` and retry once.1285. **Prerelease warnings mean stop.** If locking warns about129 prerelease resolution or would need `--prerelease` flags or130 config to accept the version, do not add flags, pins, or config131 to force it through — halt this repo and report. A correctly132 published release resolves without coaxing; a warning means133 something upstream is wrong (stale cache, missing134 `exclude-newer-package` entry, or a bad publish).1356. **Commit** — mirror the repo's own previous bump commits for this136 package (`git log --oneline --grep '<package>'`). Fallback137 subject, version-arrow form:138139 ```140 py(deps[docs]) <package> <old> → <new>141 ```142143 Body: why/what summarizing what the new release ships — read the144 package's CHANGES entry for the version, do not invent.1457. **Push** — `git push` (skip with `--no-push`). Never force.146147## Phase 5: Verify with gh148149For every pushed repo:1501511. Clear stale Actions caches so CI resolves fresh:152153 ```154 gh cache delete --all --repo <owner>/<repo>155 ```1561572. Watch the run for the pushed commit:158159 ```160 gh run list --repo <owner>/<repo> --branch <branch> --limit 1161 ```1621633. On failure: clear the cache again and164 `gh run rerun <run-id> --failed`. If it still fails, compare with165 the run before the bump — a pre-existing failure is reported as166 such, not retried forever.1674. Where the repo deploys docs from CI, confirm the docs job/workflow168 succeeded too — a docs-dependency bump that breaks the docs build169 defeats the point.170171## Output1721731. Hero line: `✓ <package> <old> → <new> across N repos` (or `⚠` with174 the failure count).1752. **Rollout summary** — per repo: branch, prep commits, bump commit,176 pushed or local, CI status.1773. **Stopped repos** — any halted on prerelease warnings, dirty178 trees, or persistent CI failures, each with what the user should179 look at.1804. Next-step `ask-user-choice` panel: rerun failed CI checks / retry181 stopped repos / push the `--no-push` commits / done.182183184## Portability notes185186- `ask-user-choice` — present the listed options and wait for the user to pick one. Hosts with a structured multiple-choice tool (Claude Code's `AskUserQuestion`) should use it; otherwise print a numbered list and wait for a numbered reply. Never proceed on an assumed answer.187- `$ARGUMENTS` — the text the user passed when invoking this skill. If your host does not substitute it, read it as the user's request in the current turn, and ask when there is none.