--- name: update-deps description: Dependabot-aware dependency updates with security audit, real-CI validation, and a unified PR. Framework-agnostic. disable-model-invocation: true --- # Update Dependencies Update project dependencies safely: reconcile open Dependabot PRs into one unified change, run the project's security audit, validate boot-affecting changes against the real CI environment, and open a single PR — instead of stopping at local commits. This skill is framework-agnostic. It **detects** the project's package manager, test/lint commands, audit suite, and CI workflow rather than assuming a stack. ## Command Options - `--dry-run`: Show what would be updated without making changes - `--major`: Include major version updates (default: minor/patch only) - `--package `: Update specific package only - `--skip-tests`: Skip running tests between updates ## Workflow at a glance ```text Detect → Reconcile Dependabot → Update → Audit → Validate on real CI → Open PR → Verify auto-close ``` Each stage feeds the next. Don't skip the audit or the real-CI validation for boot-affecting changes — those are the two stages that catch what local tests can't. ## Your task ### 1. Detect the environment (do not hardcode) Detect both the package manager **and** the project's real entry points. The runners below are common defaults, not assumptions — always confirm against what the repo actually uses. - **Package manager**: - `package.json` (npm/yarn/pnpm) — check the lockfile to disambiguate - `Gemfile` (bundler) - `requirements.txt` / `pyproject.toml` (pip/poetry/uv) - `Cargo.toml` (cargo) - `go.mod` (go modules) - **Test/lint/CI entry points** — prefer a single project gate over assumed runners: - A wrapper script like `bin/ci`, `bin/test`, `script/test`, or a `Makefile`/`Justfile` target is the **canonical gate** — use it if present. - Otherwise detect the real tools: Minitest vs RSpec, StandardRB vs RuboCop, Herb, Biome vs ESLint, Prettier, `tsc`, `mypy`, etc. - Detect the **CI workflow** itself: list `.github/workflows/*.yml` and identify the workflow that runs on the default branch and the jobs it contains. - **Audit suite** (see step 4) — detect, don't assume. ### 2. Reconcile open Dependabot PRs Before making any changes, find out what Dependabot already has in flight: ```bash gh pr list --app dependabot --state open --json number,title,headRefName,body ``` - If open Dependabot PRs exist, **fold their bumps into one unified branch** rather than running in parallel and leaving stale PRs behind. - Land **the exact target versions or newer** for every bump a Dependabot PR covers. Dependabot **auto-closes** a PR once its target versions (or higher) land on the base branch — landing a lower version defeats that and leaves the PR open. - Note which PRs you expect to be superseded; you'll verify their auto-close in step 7. ### 3. Update incrementally Keep the safe, category-by-category loop: - **Security updates first** (always include), then **patch**, then **minor**. - **Major updates** only with `--major`. Isolate a risky major into **its own commit — or its own PR** — so it can be rolled back cleanly without reverting the safe bumps. - Run the detected test gate after each category. **If `--skip-tests`**: skip test execution. - For major bumps, flag **soft-dependency and default-behavior changes** explicitly — a newly pulled-in transitive dependency, a changed default load/eager-load behavior (e.g. Bundler's `require:`), a renamed config key. These are where "the tests pass but boot breaks" lives. - Stop and report if the gate fails; offer to revert the specific update that caused it. ### 4. Run the security audit This is the highest-value stage on a dependency PR — not an afterthought. After updates, run the project's detected audit suite and **fold any newly surfaced fix into this PR** (a fresh advisory is in-scope, not a separate task): | Ecosystem | Audit tools (detect what's present) | | --------- | ----------------------------------- | | Ruby | `bundler-audit`; plus `brakeman` and `importmap audit` on Rails | | Node | `npm audit`, `yarn npm audit`, `pnpm audit` | | Python | `pip-audit`, `safety` | | Rust | `cargo audit` | | Go | `govulncheck` | If the project's CI gate (e.g. `bin/ci`) already runs these, run that gate rather than invoking each tool separately. ### 5. Validate boot-affecting changes on the real CI A change that touches the **manifest** (the `Gemfile`, `package.json`, `pyproject.toml` — not just the lockfile) can alter what loads at boot. Local gates **cannot** catch a failure caused by a system library that happens to be installed on the dev machine but missing on one CI job. Validate those changes on the actual CI environment before recommending merge: ```bash gh workflow run --ref gh run watch # or: gh run list --branch ``` Do this when: - The manifest changed (new dependency, changed `require:`/load behavior, version constraint change), **and/or** - A bump pulls in a native/system-backed library (image processing, crypto, database drivers). Lockfile-only patch bumps with a green local gate generally don't need a dedicated CI run. ### 6. Open a unified PR Stop-at-commits is not the finish line. Open **one** PR for the reconciled set, following the project's PR conventions — hand off to the `/create-pr` skill (it generates the description, links issues, and updates the ROADMAP). Summarize in the PR body: the bumps grouped by category, the Dependabot PRs this supersedes, any CVE fixed, and the result of the real-CI run. ### 7. Verify Dependabot auto-close (don't race it) After the PR merges, **expect** Dependabot to auto-close the superseded PRs on its own — verify it did rather than pre-emptively closing them: ```bash gh pr list --app dependabot --state open ``` - If a superseded PR closed automatically: done. - **Fallback only:** if a PR is still open after Dependabot's next rebase cycle — the edge where a lower/different version landed and Dependabot just rebases instead of closing (see `dependabot/dependabot-core#13606`) — close it manually with a note. Don't duplicate Dependabot's behavior or race its rebase. ## Design principles The lessons this workflow encodes, worth keeping in mind when a situation doesn't fit the steps above: - **"Passes locally, fails on one CI job" is a real failure class.** A local gate can't see a system library that's present on your machine but absent on a specific CI job. Boot-affecting dependency changes need real-CI validation, not just local tests. - **Don't fight Dependabot.** It already reconciles bumps and auto-closes superseded PRs. The skill's job is to _expect and verify_ that behavior, not re-implement it. - **Security audits are the highest-value step**, not an afterthought. A plain update can ship a live CVE that only the audit catches. ## Package manager commands ### npm/yarn/pnpm ```bash npm outdated # check npm update # update ``` ### Ruby (Bundler) ```bash bundle outdated bundle update ``` ### Python (pip/poetry) ```bash pip list --outdated poetry show --outdated && poetry update ``` ### Rust (Cargo) ```bash cargo outdated cargo update ``` ### Go ```bash go list -u -m all go get -u ``` ## Update categories 1. **Security updates** (always applied) — packages with known vulnerabilities. 2. **Patch updates** (default) — bug fixes, e.g. `1.2.3 → 1.2.4`. 3. **Minor updates** (default) — backwards-compatible features, e.g. `1.2.3 → 1.3.0`. 4. **Major updates** (only with `--major`) — breaking changes, e.g. `1.2.3 → 2.0.0`. Isolate for clean rollback. ## Commit message format Use Conventional Commits format from global CLAUDE.md: ```text chore(deps): update dependencies Security updates: - erb: 6.0.1 → 6.0.3 (CVE-2026-41316) Patch updates: - rails: 7.1.3 → 7.1.5 Minor updates: - image_processing: 1.12 → 2.0 Breaking changes: - image_processing 2.0 requires ruby-vips; added with require: false so a missing libvips does not crash boot. ``` ## Error handling - **No package manager detected**: "No supported package manager found" - **No outdated packages and no open Dependabot PRs**: "All dependencies are up to date" - **Test or audit failure**: identify the culprit update, offer to revert it - **Real-CI run fails**: report the failing job; do not recommend merge - **Lock file conflicts**: guide through resolution ## Integration with global standards Follow dependency best practices from global CLAUDE.md: - Pin versions, use lock files for consistent environments - Test breaking changes thoroughly; document system requirements - Treat security advisories as in-scope for the dependency PR