Autonomous superpowered Dependabot. Auto-discover all outdated packages, audit overrides, apply codebase migrations for major bumps, resolve dependency conflicts, and run the quality gate. No user prompts — just execute.
Pre-flight: Worktree check
This wrapper writes a new pnpm-lock.yaml and opens a PR — both belong on the main checkout, not a per-SPEC worktree branch. If invoked from a linked worktree, reject hard with a message that surfaces the cached state from main so the user knows whether action is even pending.
Detection (run this first, before anything else):
common_dir="$(git rev-parse --git-common-dir 2>/dev/null)"
if [ -n "$common_dir" ]; then
case "$common_dir" in
/*) absolute_common_dir="$common_dir" ;;
*) absolute_common_dir="$(pwd)/$common_dir" ;;
esac
main_root="$(cd "$(dirname "$absolute_common_dir")" 2>/dev/null && pwd)"
current_root="$(git rev-parse --show-toplevel 2>/dev/null)"
if [ -n "$main_root" ] && [ -n "$current_root" ] && [ "$main_root" != "$current_root" ]; then
cached_line="Cached state unavailable on main; symlinks may be broken — run \`gaia setup link-worktree\` to repair."
cache_file="$main_root/.gaia/cache/update-check.json"
if [ -f "$cache_file" ] && command -v jq >/dev/null 2>&1; then
outdated_count="$(jq -r '.outdatedCount // 0' "$cache_file" 2>/dev/null)"
checked_at="$(jq -r '.checkedAt // 0' "$cache_file" 2>/dev/null)"
if [ -n "$outdated_count" ] && [ -n "$checked_at" ] && [ "$checked_at" != "0" ]; then
now=$(date +%s)
age=$((now - checked_at))
# Format age as <Nm ago> / <Nh ago> / <Nd ago>.
ago_unit="s"; ago_value="$age"
if [ "$age" -ge 86400 ]; then ago_unit="d"; ago_value=$((age / 86400));
elif [ "$age" -ge 3600 ]; then ago_unit="h"; ago_value=$((age / 3600));
elif [ "$age" -ge 60 ]; then ago_unit="m"; ago_value=$((age / 60));
fi
cached_line="Cached on main: $outdated_count packages outdated (last checked ${ago_value}${ago_unit} ago)."
fi
fi
cat <<EOF
/update-deps must run from the main checkout, not a worktree.
Worktree: $current_root
Main checkout: $main_root
$cached_line
Run \`cd $main_root\` then re-invoke /update-deps.
EOF
exit 1
fi
fi
If the detection does not fire, fall through to the existing ## Pre-flight: Branch check section.
Pre-flight: Branch check
git branch --show-current
If the current branch is main or master and not running in CI, set a flag (SHOULD_CREATE_BRANCH=true) but do not create the branch yet — branch creation is deferred until after Phase 1 confirms there are packages to update. Creating a branch when there is nothing to update pollutes the branch list.
In CI (CI=true, set by GitHub Actions, GitLab CI, CircleCI, and most CI providers), skip branch creation — the workflow owns branch management and pre-creates the appropriate branch before this skill runs.
Otherwise set SHOULD_CREATE_BRANCH=false and proceed on the current branch.
Composition: --scope <group-name>
When invoked with --scope <group-name> (e.g. /update-deps --scope react-router):
- Skip Phase 0 (override audit) — out of scope for a single-group run.
- Skip Phase 1 (discovery) — the group's members are known from the companion-group table.
- Skip Phase 3 (wave classification) — the run is implicitly a single group; treat it as Wave A if all members are minor/patch, else Wave B.
- Phase 4 / Phase 5 still apply, scoped to the named group's members
in root
package.json. - Quality gate, return value, and final report still run.
Used by the GAIA CI update-deps workflow's wave-B matrix shards to fan out one PR per major-bump group.
Phase 0–4: Haiku agent
Spawn a Haiku agent (model: "haiku") to run Phases 0–4. Pass it these instructions verbatim:
Phase 0: Override audit
For each key in pnpm.overrides (in package.json):
- Temporarily remove that single key from
pnpm.overrides. - Run
pnpm install. - Run
pnpm ls 2>&1and scan for peer-dep errors. - If no errors → override is obsolete. Leave it removed. Note as removed in final report.
- If errors → restore that key. Note as retained in final report.
Operate on one key at a time. Always pnpm install after each toggle.
Phase 1: Discover outdated packages
pnpm outdated --json
Parse the JSON. For each entry record:
namecurrentversionlatestversionis_major_bump(compare leading integers)is_pinned(no^or~prefix in the spec found inpackage.json)
ESLint cap: if eslint or @eslint/js show a latest whose major is >= 10, find the highest available 9.x (pnpm view eslint versions --json and pick the highest 9.x.y) and treat that as the target. If already on the latest 9.x, drop the entry.
Apply this silently. Capped packages MUST NOT appear anywhere in the final report — not in Updated, not in Skipped, not in Breaking changes. Adopters know about the cap; surfacing it on every run is noise.
If nothing is outdated after this filtering, print All packages are up to date. and exit.
Phase 2: Resolve companion groups
Map each outdated package into its group. When any member of a group is outdated, include all members present in package.json in the update — even ones not flagged outdated — so the group moves together.
| Group | Members |
|---|---|
react-router | react-router, react-router-dom, @react-router/dev, @react-router/node, @react-router/serve, @react-router/fs-routes, @react-router/remix-routes-option-adapter |
react | react, react-dom, @types/react, @types/react-dom |
tailwindcss | tailwindcss, @tailwindcss/vite, @tailwindcss/forms, @tailwindcss/typography, prettier-plugin-tailwindcss |
storybook | storybook, @storybook/*, eslint-plugin-storybook, msw-storybook-addon, storybook-react-i18next, @vueless/storybook-dark-mode |
vitest | vitest, @vitest/coverage-v8, @vitest/ui, @vitest/eslint-plugin |
playwright | @playwright/test, @playwright-testing-library/test |
eslint | eslint, @eslint/js, @eslint/compat, eslint-config-*, eslint-plugin-* (9.x cap applies) |
testing-library | @testing-library/dom, @testing-library/react, @testing-library/jest-dom, @testing-library/user-event |
typescript | typescript, @types/node |
i18next | i18next, react-i18next, remix-i18next, i18next-browser-languagedetector |
msw | msw, msw-storybook-addon |