Skip to content

Commit 8f5b0e9

Browse files
committed
docs: add update-major-deps skill
1 parent bda7db9 commit 8f5b0e9

2 files changed

Lines changed: 148 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
description: Perform major dependency updates one logical group at a time, assessing breakage risk, then build, test, and create a signed commit per group
3+
args: `[scope: all | <package-name>]`
4+
---
5+
6+
You are performing **major** version updates of devDependencies in this monorepo, one logical group at a time. For each group you assess breakage risk, apply the bump, build, test, and — only when green — create a single signed commit. You **never push**; the user pushes themselves.
7+
8+
**Input**
9+
10+
- No args, or `all` → process every available major update.
11+
- `<package-name>` → process only the group containing that package (e.g. `@babel/core` processes the whole `@babel/*` group).
12+
13+
**Core principles**
14+
15+
- **One logical group per commit.** Isolating each change makes regressions trivial to bisect and revert.
16+
- **Tests gate every commit.** Never commit a group whose build or tests fail.
17+
- **Commit-only.** Never `git push`. Never use `--no-verify` or otherwise bypass hooks.
18+
- **Risk-gated.** Auto-proceed for `none`/`low` risk; **pause and ask the user** before committing a `medium`/`high` risk group.
19+
20+
---
21+
22+
**Process**
23+
24+
**1. Preflight**
25+
26+
- Confirm the working tree is clean (`git status --short`). If not, stop and report — do not mix unrelated changes into dependency commits.
27+
- Confirm Node/npm are available (`node -v`, `npm -v`). This repo uses Node 22.12 via nvm; if `npm` is missing, the terminal likely hasn't loaded nvm (`source ~/.zshrc`).
28+
- Note the current branch.
29+
30+
**2. Detect and group major updates**
31+
32+
Run a dry check:
33+
34+
```
35+
npx --yes npm-check-updates --dep dev
36+
```
37+
38+
Identify entries whose **major** version increases. Then group **coupled packages** that must move together:
39+
40+
- All `@babel/*` (`@babel/cli`, `@babel/core`, `@babel/preset-env`, presets/plugins) → one group.
41+
- All `@commitlint/*` (`@commitlint/cli`, `@commitlint/config-conventional`) → one group.
42+
- Any other related family sharing a major line → one group.
43+
- Everything else → its own single-package group.
44+
45+
Order the groups **lowest risk first** (see step 3) so cheap wins land before risky changes.
46+
47+
**3. Assess breakage risk (per group)**
48+
49+
Produce a risk level of `none` / `low` / `medium` / `high` with a short rationale, using these signals:
50+
51+
- **Role in the pipeline** (biggest factor):
52+
- _Editor-only_ (e.g. `@types/*` in a JS-only repo with no `tsconfig`/`.ts` source) → **none**.
53+
- _Test-only_, using only stable core APIs → **none/low**.
54+
- _Tooling / git-hook gating_ (`lint-staged`, `husky`) → **low/medium** (can block commits, but not ship broken code).
55+
- _Commit/CI gating_ (`@commitlint/*`) → **medium** (a stricter ruleset can reject commit messages).
56+
- _Affects compile output_ (`@babel/*`, `webpack`, loaders) → **high** (regenerates `docs/**` and `dist` artifacts; broad blast radius).
57+
- **Usage breadth**: count real usage sites (`grep -rl` across `packages/*/*/src`, `packages/*/*/test`, and root config). Few sites + only core APIs → lower risk.
58+
- **Direct vs transitive**: deduped/transitive-only presence → lower risk than a directly exercised dependency.
59+
- **Changelog red flags**: dropped Node/engine support, removed or renamed APIs, stricter-by-default behavior, config-format changes.
60+
- **Engine vs active runtime** (check explicitly): compare the target version's `engines.node` against the running `node -v`. Mismatch raises risk — and is especially dangerous for tools invoked by git hooks (`lint-staged`, `commitlint`), since a hook failure blocks _every_ commit, including the bump's own commit. Resolve by upgrading Node first (e.g. `nvm install 22 --reinstall-packages-from=<current>`), then re-evaluate (risk usually drops once the engine is satisfied).
61+
62+
```
63+
npm view <pkg>@<target> engines
64+
node -v
65+
```
66+
67+
State the level and rationale before touching anything.
68+
69+
**4. Gate on risk**
70+
71+
- `none` / `low` → proceed automatically.
72+
- `medium` / `high`**stop and ask the user to confirm** before applying. Summarize the risk and what could break.
73+
74+
**5. Apply the bump (lock-preserving)**
75+
76+
Install the whole group to its new major in one command, e.g.:
77+
78+
```
79+
npm install --save-dev @babel/core@^8 @babel/cli@^8 @babel/preset-env@^8
80+
```
81+
82+
Confirm `package.json` reflects the new ranges.
83+
84+
**Preserve the lockfile — never clean-nuke.** Do **not** "fix" install problems with `rm -rf node_modules package-lock.json && npm install`. A lockfile-free resolve re-floats _unrelated_ deps to the latest in-range version, which can land on a release your registry/security proxy blocks (e.g. a `403 Forbidden` on `prettier@3.8.5` while the committed lock safely pinned `3.8.4`). Keeping the lockfile as the baseline means only the group you target changes.
85+
86+
**Handling ERESOLVE on coupled toolchains.** Upgrading a coupled family (e.g. `@babel/*` core+cli+preset-env) against an existing lock can ERESOLVE because npm anchors on the _locked_ old version (`Found: @babel/cli@7.29.7`) while installing the new one — an incremental-resolver deadlock, not a real incompatibility. Both targeted (`npm install pkg@^8`) and full (`npm install`) forms can hit it. The lock-preserving fix:
87+
88+
```
89+
npm install --legacy-peer-deps
90+
```
91+
92+
With the lockfile present, this keeps every other dep pinned (so `prettier` stays at its safe version) while letting the babel subtree move to v8. After it completes, **verify the resulting tree is self-consistent** before trusting it:
93+
94+
```
95+
npm ls @babel/core @babel/cli @babel/preset-env
96+
npm ls babel-plugin-polyfill-corejs3 # must be a major that supports core 8 (1.x)
97+
npm ls prettier # must be unchanged / safe version
98+
```
99+
100+
**6. Build and test**
101+
102+
```
103+
npm run build
104+
npm test
105+
```
106+
107+
- **Green** → continue to commit.
108+
- **Red** → stop. Report the failure, leave the changes in the working tree for inspection, and do **not** commit. Suggest next steps (read changelog/migration guide, adjust config). Do not attempt unrelated fixes.
109+
110+
**7. Commit (signed, commit-only)**
111+
112+
Stage exactly the files this group changed (`package.json`, `package-lock.json`, and any legitimately regenerated build artifacts). For compile-affecting groups like `@babel/*` this includes the regenerated `docs/**/*.min.js(.map)` bundles **and** `packages/**/dist/cjs/index.js` outputs — stage them explicitly (`git add package.json package-lock.json docs packages`). Review with `git status --short` first. **Never** `git add -A` and never sweep in unrelated working-tree changes (e.g. skill files under `.claude/` or `CLAUDE.md`).
113+
114+
Use a Conventional Commits message (the repo enforces commitlint + a husky `commit-msg` hook). Examples:
115+
116+
```
117+
build(deps-dev): bump @types/node from 25 to 26
118+
build(deps-dev): bump @babel/core, @babel/cli and @babel/preset-env from 7 to 8
119+
```
120+
121+
Commits are GPG-signed automatically (`commit.gpgsign=true`). Verify with:
122+
123+
```
124+
git log -1 --format='%h %G? %an <%ae>'
125+
```
126+
127+
`%G?` should print `G` (good signature). **Do not push.**
128+
129+
**8. Repeat and report**
130+
131+
Move to the next group. When done (or stopped), output a summary table — one row per group, ordered as processed:
132+
133+
| Group | Risk | Build/Test | Commit |
134+
| ----------------------- | -------------------- | ---------- | ------------- |
135+
| `<pkg>` `<old>``<new>` | none/low/medium/high | pass/fail | `<short-sha>` |
136+
137+
List any groups that were skipped (risk not confirmed) or failed (left uncommitted for follow-up).
138+
139+
---
140+
141+
**Safety reminders**
142+
143+
- Never `git push`.
144+
- Never bypass hooks (`--no-verify`) or force anything.
145+
- Never bundle multiple groups into one commit.
146+
- Never clean-nuke the lockfile to resolve an install error — it can float unrelated deps to forbidden/newer versions. Prefer lock-preserving installs (`--legacy-peer-deps` with the lock present for coupled-toolchain ERESOLVE).
147+
- If the working tree starts dirty or a build/test fails, stop rather than working around it.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ The following slash commands are available for common development tasks. Run the
88
- `/test-coverage` — audit unit test coverage for a module and produce a prioritized gap report
99
- `/add-unit-tests` — write missing unit tests based on coverage gaps; can accept a gap report from `/test-coverage` to skip re-running coverage
1010
- `/revamp-demo` — rewrite a module's demo page with consistent style, structure, and on-page output
11+
- `/update-major-deps` — perform major devDependency updates one group at a time with risk assessment, build, test, and a signed commit per group (commit-only, pauses on medium/high risk)
1112

1213
Skills are defined in `.claude/skills/`.

0 commit comments

Comments
 (0)