update-deps
Update every package's dependencies across the monorepo (packages, apps, examples, templates, python/*, and .github/workflows/*), regenerate lockfiles, and create a chore: update dependencies changeset for the JS side.
JS / TS (pnpm workspaces)
Preview what would change without writing anything:
pnpm deps:check
Run the full update (writes package.json files, reinstalls, dedupes, generates the changeset):
pnpm deps:update
Both are defined in the root package.json; deps:update runs scripts/update-deps.sh, which performs, in order:
npx taze major -f -w -r— bump every dependency (incl. major) recursively.- Wipe the
node_modulesof every tracked package andpnpm-lock.yaml. The wipe is driven fromgit ls-files, so a git worktree checked out under the repository keeps its own installed tree. pnpm install --no-frozen-lockfile— required before the repin:expo install --fixresolves the installed versions off the file system and compares those against the SDK matrix, so it has to see a tree resolved from the manifest taze just wrote. It has to be this fresh resolve, because an install that still had the old lockfile to reuse would resolve back to the versions taze replaced and the repin would inspect a tree that will never ship.npx expo install --fixinsideexamples/with-expo— required: taze does not know about Expo's SDK compatibility matrix and will bumpexpo-*/react-native-*/react/react-domto versions that crash at runtime.expo install --fixre-pins them to the versions sanctioned by the currentexpoSDK. Do not skip this step, and do not commit Expo-related bumps without it. When either step fails the script restores the entries in the SDK's native-module matrix fromexamples/with-expo/package.json, because taze's Expo bumps are unsanctioned without the repin, finishes the remaining steps, and exits non-zero.pnpm install+pnpm dedupe— reconcile the lockfile with whatever the repin rewrote.bash scripts/generate-deps-changeset.sh— write a patch changeset for each published package whosepackage.jsonchanged.
Expo notes
- If you bump the
expomajor inexamples/with-expo(e.g. SDK 55 → 56),expo install --fixwill rewrite the matchingreact,react-dom,react-native,react-native-*, andexpo-*versions. Eyeball the diff inexamples/with-expo/package.jsonto confirm everything snapped to the expected SDK line. - If you intentionally want to hold Expo back, run
pnpm deps:update, thengit checkout examples/with-expo/package.jsonand re-runpnpm install+ the changeset script manually. examples/with-expopins@react-native/metro-configas an explicit devDependency locked to the same version asreact-native(e.g.0.86.3). This exists only to steer pnpm's optional-peer resolution for@react-native/community-cli-plugin's exact peer on it — the example never imports@react-native/metro-configdirectly (metro.config.jsusesexpo/metro-config) — so nothing fails loudly if it drifts.taze major -fwill happily bump it independently ofreact-native; after each run, confirm it still matchesreact-native's version (SDK 57 → RN0.86.3→ metro-config0.86.3) and hand-correct it if taze moved it ahead.expo install --fixupgrades theexpopackage itself before it repins anything, so a release inside pnpm'sminimumReleaseAgewindow (pnpm-workspace.yamlsets 1440 minutes) makes it, or the install that precedes it, exit non-zero having applied nothing. The script then restores the entries keyed inexpo/bundledNativeModules.json, read rather than hardcoded so the set tracks the SDK, unioned with anexpo/expo-*/@expo/*/react/react-native*name pattern so an SDK package missing from the matrix is still covered, because the floor taze wrote matches only the age-blocked release and keeping it would fail the fresh resolve that follows. That is the matrix half of whatexpo install --fixconsults; therelatedPackageshalf (@babel/core,@types/react,typescriptamong them) comes from Expo's versions endpoint over the network, so those keep taze's bumps and are reviewed like any other bump. The run still exits non-zero, because the repin is required. Rerun the update once the release has aged.
Workflow
- From a clean working tree on a feature branch, run
pnpm deps:update. It takes several minutes (lockfile is regenerated from scratch). git statusto confirm the changeset file appeared under.changeset/and that onlypackage.json/pnpm-lock.yamlfiles changed.- Validate before committing:
pnpm build pnpm lint pnpm test - If a package breaks on a major bump, pin that one dep back in the offending
package.jsonand re-runpnpm install; the changeset script does not need to re-run. - Commit as
chore: update dependenciesand push.
Notes
- Do not hand-edit the generated changeset's bump levels —
generate-deps-changeset.shcorrectly emitspatchfor every published package whosepackage.jsonchanged and skips private packages (@assistant-ui/docs,@assistant-ui/shadcn-registry, etc.). PerAGENTS.md, dependency updates are always patch. - The script detects changes via
git diff HEAD, so run it with the package.json edits still unstaged (or staged — it checks both). Don't commit before it runs. pnpm-lock.yamlwill have a huge diff; that's expected since step 2 deletes it.pnpm unmanaged-pins:checkguards two pins the updater never opens, so a routine run can go red on a file it did not touch. Raise the exact pins inapps/docs/lib/xulux/learn/courses/*/shared/project/package.jsonto whatever the workspace now prevailingly declares (pins on packages this repository publishes are exempt, since those move on every release), and move any version-scopedallowBuildsentry inpnpm-workspace.yamlto the version the refreshed lockfile installs.- Node
>=24andpnpm@12.1.0are required (see rootpackage.jsonengines/packageManager).
Python (uv)
Python packages live under python/ and each has its own pyproject.toml + uv.lock. They are not touched by pnpm deps:update.
Packages:
python/assistant-streampython/assistant-ui-sync-server-apipython/assistant-transport-backendpython/assistant-transport-backend-langgraphpython/state-testpython/assistant-stream-hello-world(no lockfile — example)
For each package with a uv.lock, upgrade with:
cd python/<package>
uv lock --upgrade
uv sync
uv run pytest # if tests exist
Or in one pass from the repo root:
for d in python/*/uv.lock; do
(cd "$(dirname "$d")" && uv lock --upgrade && uv sync)
done
Notes:
- Python bumps do not require a changeset — Python packages are versioned manually in their
pyproject.tomland published via.github/workflows/pypi-publish.yaml, independent of the JS changesets pipeline. - Bumping a published Python package's own version (e.g.
assistant-stream) is a separate release decision;uv lock --upgradeonly touches transitive deps. - Commit Python and JS dep updates separately if the diff is large, or as one
chore: update dependenciescommit if both are clean.
GitHub Actions
Every uses: entry under .github/workflows/*.{yml,yaml} is SHA-pinned for supply-chain hardening and carries a ratchet: marker naming the release the SHA came from:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v7.0.1
The marker is what ratchet update reads to recognize the entry, so an entry that loses it keeps working and stops being updated. pnpm unmanaged-pins:check fails on a ref that is not a commit SHA, on a missing marker, on a marker with no version, and on one that names a different action than its uses: ref. Annotate a deliberately unmanaged ref, or a pinned container image, with # ratchet:exclude, which waives all four.
There is no Dependabot config (.github/dependabot.yml does not exist), so these don't update themselves. pnpm deps:update does not touch them either.
To refresh them, use ratchet (or pinact):
# pin any remaining tag refs to SHAs (one-time per file)
ratchet pin .github/workflows/*.yml .github/workflows/*.yaml
# bump every SHA-pinned action to the latest release SHA for its major
ratchet update .github/workflows/*.yml .github/workflows/*.yaml
Both rewrite the marker alongside the SHA. If ratchet isn't available, fall back to manually checking each uses: against the action's releases page and updating the SHA and marker together.
After updating, sanity-check on a branch by pushing and watching the affected workflows actually run (most are PR-triggered: code-quality, autofix, changeset, changeset-semver-check, expo, devtools-frame, registry). Release workflows (npm-publish, pypi-publish, traction) can't be tested without a release tag — eyeball those diffs extra carefully.
Notes:
- GH Actions updates do not need a changeset (they don't ship in any npm package).
pnpm unmanaged-pins:checkalso holds the Node.js major consistent across every workflow, so bumpruntime: node@Nandnode-version:together.- Commit as
chore: update github actions(or roll intochore: update dependenciesif landing alongside the JS/Python bumps). - If a major bump changes inputs/outputs, check the action's release notes —
ratchetwill happily move you fromv4tov6without warning about breaking changes.