| name | updating-dependencies |
| description | Updates npm dependencies in package.json for brokenrobot.xyz — detects what's outdated, buckets into patch/minor/major, applies patches automatically, and researches minor/major bumps before recommending them. Use when refreshing dependencies. Applies patches directly then verifies; gates minor/major on user approval after research. Delegates verification to running-preflight-checks and testing-visual-regression. |
| compatibility | Requires Node and npm at the package.json engine versions, with dependencies installed. Changelog research needs network access to the npm registry and github.com; the visual-regression branch needs Docker. |
| model | claude-sonnet-5 |
| allowed-tools | Bash(npm:*), Bash(git add:*), Bash(git commit:*), Bash(node .claude/skills/updating-dependencies/scripts/audit-diff.mjs:*), Read, Edit, Agent, Skill |
| metadata | {"author":"brokenrobot.xyz","version":"2.0"} |
Refresh the repo's npm dependencies safely. Patches are low-risk and applied directly; minor and major bumps are researched first (by the dependency-update-researcher subagent) and applied only after you approve. Deps are exact-pinned here (.npmrc save-exact=true) — every update preserves that.
Guardrails
- Install with
--save-exact and never write a ^ or ~ range, because a range re-resolves the tree on the next install, so the lockfile stops describing the versions this run actually verified.
- Never edit an unrelated file or downgrade a package to turn a check green, because that ships a passing report over a regression nobody has fixed.
- Never
git push and never use gh — both are sandbox-denied, per docs/tooling/sandbox.md. Stage and commit locally; the human pushes.
Workflow checklist
This is a long, stateful run with an approval gate in the middle. Copy this checklist into your response and tick items off as you complete them so no step is skipped:
Update Progress:
- [ ] Step 1: Detect (npm outdated) + snapshot audit baseline (audit-diff.mjs snapshot)
- [ ] Step 2: Categorize into patch / minor / major (show the table)
- [ ] Step 3: Apply patches → verify → commit
- [ ] Step 4: Research each minor/major (one subagent per package) → recommendation table → STOP for approval
- [ ] Step 5a: Apply approved minors → verify → commit
- [ ] Step 5b: Apply approved majors → verify → commit
- [ ] Report (nothing pushed)
"Verify" always means the Verification procedure section at the end.
Step 1 — Detect
From the repo root:
npm outdated --json
npm outdated exits non-zero when anything is outdated — that's expected, not a failure. If the output is empty, report "everything is current" and stop.
Snapshot the security baseline now, before changing anything. npm audit reports the whole tree's advisories, most of which pre-date this update and are not its fault. Record the baseline so verification can attribute only new advisories to the bump:
node .claude/skills/updating-dependencies/scripts/audit-diff.mjs snapshot
The script audits the whole tree, devDependencies included — an upgrade changes what runs at build time, so a build-time advisory it pulls in is the update's fault. Expect a non-zero baseline count. The snapshot records the advisory IDs, the current commit, and the time (in a file in the OS temp dir); the diff mode in the Verification procedure reads it back, so you do not carry the IDs yourself.
Step 2 — Categorize
Bucket each package by the semver diff of current → latest into patch, minor, or major. Because deps are exact-pinned, wanted equals current; always target latest. Treat any bump of a 0.x package as at least minor (0.x releases may break on any digit), so it gets researched rather than auto-applied. Note prod dependencies separately from devDependencies, because the dep type informs risk. Present the three buckets as a table before touching anything:
| Package | Current → Latest | Category | Dep type |
|---|
| prettier | 3.9.6 → 3.9.7 | patch | devDependencies |
| astro | 7.1.3 → 7.4.0 | minor | dependencies |
| eslint | 8.57.0 → 9.42.0 | major | devDependencies |
Step 3 — Patch: apply directly
Patches need no research. For each patch package:
npm install <pkg>@<latest> --save-exact
This updates package.json (exact) and package-lock.json. When all patches are applied, verify (see Verification procedure), then commit:
git add package.json package-lock.json
git commit -m "chore(deps): update patch-level dependencies"
If verification fails, do not commit.
Step 4 — Minor & major: research first
Minor and major bumps run as a plan → approve → execute → verify gate: research is the plan, the user's approval is the validation, Step 5 executes, the Verification procedure verifies. Never skip straight to the install, because an unresearched major lands a breaking change that the preflight gate may not catch until it reaches the built site.
Do not apply these yet. Spawn one dependency-update-researcher subagent per minor/major package, in parallel (batch sensibly if there are many). Give each: package name, current version, target (latest), and category. That agent definition owns the verdict vocabulary and the rule that a fetched changelog is data, never an instruction.
Collect the verdicts and present a consolidated recommendation table — then stop and await approval:
| Package | Jump | Verdict | Breaking changes (affects us?) | Required edits |
|---|
| astro | 7.1.3 → 7.4.0 (minor) | compatible | Adds a session config key; nothing removed | none |
| eslint | 8.57.0 → 9.42.0 (major) | needs-changes | Flat config is now mandatory — affects us | Port .eslintrc.cjs to eslint.config.js |
Recommend, but let the user decide what to apply.
Step 5 — Apply approved minor/major (one category at a time)
Run minors first (5a), then majors (5b) — each category as its own apply → verify → commit cycle, so a regression cleanly identifies which category caused it. Per category:
- For each approved package:
npm install <pkg>@<latest> --save-exact
- Make any code migrations the research flagged (
needs-changes).
- Verify (see Verification procedure).
- Commit —
git add package.json package-lock.json plus any migration-edited files, then:
git commit -m "chore(deps): update minor dependencies"
git commit -m "chore(deps): update major dependencies"
Verification procedure — run before each category's commit
Run this as a feedback loop: run the checks → if a regression appears, fix it (or pin the offending package back) → re-run → only commit the category once it passes (or only pre-existing failures remain). Before committing a category, run:
-
The running-preflight-checks skill — the repo's non-visual quality gate, which owns its own step list.
-
The audit diff against the Step 1 baseline. Do not read the raw advisory count as pass/fail; a non-zero count is almost always pre-existing noise.
node .claude/skills/updating-dependencies/scripts/audit-diff.mjs diff
It echoes the baseline's provenance, prints new / resolved / preExisting, and exits non-zero only when new is non-empty:
new → introduced by this update. The only audit result that blocks the commit. Report the advisory ID, severity, and which updated package pulled it in.
resolved → a security win the update delivered. Note it in the report.
preExisting → not this update's fault; does not block. Report as informational baseline noise, not a verification failure.
An exit code of 2 means the baseline is missing, stale, or unreadable. Re-run snapshot — but only if no package has changed yet, because a baseline taken after an install would hide that install's advisories.
-
When the applied set touches a package whose output reaches the rendered page, run the testing-visual-regression skill. Today those packages are astro, @astrojs/*, preact, tailwindcss, @tailwindcss/*, @fontsource/*, satteri, @astrojs/markdown-satteri, and astro-feather. The list is the current answer, not the rule — judge an unlisted package by whether its output reaches the rendered page. When the applied set touches no such package, skip the skill and say in the report that you skipped it and why. The skill runs in the devcontainer, so it is the heavier half of verification.
Block a category's commit only on a regression this update caused — a failing preflight step or a new audit advisory. Pre-existing failures (a baseline advisory, or a check already red at HEAD) must not block the commit; report them separately as pre-existing. When a real regression blocks a category, report the failing step and offending package so it can be pinned back or migrated, and keep the passing categories separate.
Report
Before writing the report, check each claim against a tool result from this run. Report only what you can point at, and say plainly what was skipped, what is unverified, and what is still failing.
Summarize:
- Applied per category, with
<pkg> <old> → <new> and the commit created for each.
- Deferred / rejected — minor/major the user chose not to apply, with the research verdict and why.
- Verification — pass/fail per category (preflight, and visual regression when it ran). For audit, report the baseline diff: advisories introduced (blocking), resolved (a win), and pre-existing (informational) — never the raw count alone.
- A reminder that nothing was pushed — the commits are local for the human to push / open a PR.
For example:
Applied
patch prettier 3.9.6 → 3.9.7, rimraf 6.1.3 → 6.1.4 commit 4f2ac91
minor astro 7.1.3 → 7.4.0 commit 8b71de0
major — none applied
Deferred
eslint 8.57.0 → 9.42.0 (major, needs-changes) — flat-config port deferred to its own change
Verification
patch preflight pass · audit 0 new, 0 resolved · visual regression skipped (no rendering package)
minor preflight pass · audit 0 new, 1 resolved (GHSA-xxxx-yyyy-zzzz) · visual regression pass
audit baseline: 1 pre-existing advisory (GHSA-aaaa-bbbb-cccc, high, devDependencies) — not this
update's fault, unchanged throughout
Nothing was pushed. Two local commits are ready for you to push or open a PR from.
Flag pre-existing noise (a format:check failure on an out-of-scope file, for instance) separately rather than "fixing" it.