home / skills / georgekhananaev / claude-skills-vault / upgrade-packages-js

upgrade-packages-js skill

/.claude/skills/upgrade-packages-js

This skill safely upgrades JavaScript packages across npm/pnpm/yarn with breaking-change detection, migration guidance, and codemod-driven code updates.

npx playbooks add skill georgekhananaev/claude-skills-vault --skill upgrade-packages-js

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
20.2 KB
---
name: upgrade-packages-js
description: Safely upgrade JavaScript packages with breaking change detection, migration guidance, and automated code migrations (npm/pnpm/yarn). Cross-platform with git safety branch enforcement.
version: 2.5.0
author: Palakorn V. <[email protected]>
tags: [npm, pnpm, yarn, upgrade, dependencies, semver, migration, codemod, monorepo]
triggers: [upgrade packages, update dependencies, check outdated, dependency upgrade]
---

# Skill: Package Upgrade (Cross-Platform)

Safely upgrade JavaScript packages while minimizing breaking change risk, with migration guidance and automated code upgrades.

---

## Non-Negotiables

1. **Verify JavaScript project first** - Stop immediately if not a JS/Node.js project
2. **Never force upgrade all at once** - Upgrade incrementally
3. **Detect package manager first** - npm/pnpm/yarn (including version)
4. **Never mix package managers** - Refuse if lockfiles conflict
5. **Git safety gate first** - Clean repo + new branch before upgrades
6. **Breaking-change evidence check** - For majors, check Releases/CHANGELOG/Migration/Codemods
7. **For breaking upgrades: migrate code + tests** - Apply codemods, implement fixes, add tests
8. **Check peer dependencies** - Ensure compatibility before upgrading
9. **Check Node.js engine compatibility** - Verify target versions support project's Node
10. **Check ESM/CJS compatibility** - Verify module system compatibility
11. **Test after each major upgrade** - Run build and tests
12. **Keep lockfile updated** - Commit lockfile changes
13. **Document skipped packages** - Record why packages were not upgraded
14. **Time-box upgrades** - Max time per major; if stuck, defer and document

---

## Project Language Detection (MANDATORY - RUN FIRST)

**This skill is for JavaScript/TypeScript (Node.js) projects only.** Run this check before anything else.

```bash
node -e "
const fs=require('fs'),p=require('path'),cwd=process.cwd();
const checks=[
  ['package.json','javascript/typescript','This skill supports this project type'],
  ['go.mod','go','Use: go get -u ./... or go-mod-upgrade'],
  ['Cargo.toml','rust','Use: cargo update or cargo-upgrades'],
  ['requirements.txt','python','Use: pip-review --auto or pip-upgrader'],
  ['pyproject.toml','python','Use: poetry update or pdm update'],
  ['Pipfile','python','Use: pipenv update'],
  ['composer.json','php','Use: composer update'],
  ['Gemfile','ruby','Use: bundle update'],
  ['pubspec.yaml','dart','Use: dart pub upgrade'],
  ['pom.xml','java','Use: mvn versions:use-latest-releases'],
  ['build.gradle','java','Use: gradle dependencyUpdates'],
  ['*.csproj','dotnet','Use: dotnet outdated or nukeeper'],
  ['Package.swift','swift','Use: swift package update'],
  ['mix.exs','elixir','Use: mix deps.update --all'],
];
const found=checks.filter(([f])=>f.includes('*')?require('fs').readdirSync(cwd).some(x=>x.endsWith(f.slice(1))):fs.existsSync(p.join(cwd,f)));
if(found.length===0){console.error('ERROR: No recognized project files found');process.exit(1)}
const js=found.find(([_,l])=>l==='javascript/typescript');
if(js){console.log('OK: JavaScript/TypeScript project detected (package.json found)');process.exit(0)}
const other=found[0];
console.error('ERROR: This skill is for JavaScript/TypeScript (Node.js) projects only.');
console.error('Detected: '+other[0]+' ('+other[1]+' project)');
console.error('Suggestion: '+other[2]);
process.exit(2);
"
```

### Supported vs Unsupported Projects

| File | Language | Supported | Alternative Command |
|------|----------|:---------:|---------------------|
| `package.json` | JavaScript/TypeScript | ✅ Yes | This skill |
| `go.mod` | Go | ❌ No | `go get -u ./...` |
| `Cargo.toml` | Rust | ❌ No | `cargo update` |
| `requirements.txt` | Python | ❌ No | `pip-review --auto` |
| `pyproject.toml` | Python (Poetry/PDM) | ❌ No | `poetry update` / `pdm update` |
| `composer.json` | PHP | ❌ No | `composer update` |
| `Gemfile` | Ruby | ❌ No | `bundle update` |
| `pom.xml` | Java (Maven) | ❌ No | `mvn versions:use-latest-releases` |
| `build.gradle` | Java (Gradle) | ❌ No | `gradle dependencyUpdates` |
| `pubspec.yaml` | Dart/Flutter | ❌ No | `dart pub upgrade` |
| `*.csproj` | .NET | ❌ No | `dotnet outdated` |
| `Package.swift` | Swift | ❌ No | `swift package update` |
| `mix.exs` | Elixir | ❌ No | `mix deps.update --all` |

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | JavaScript/TypeScript project - proceed with skill |
| 1 | No recognized project files found |
| 2 | Non-JavaScript/TypeScript project detected - use alternative |

**IMPORTANT:** If exit code is not 0, STOP and inform the user. Do not proceed with the rest of this skill.

---

## PM Command Reference

Use `{PM}` = detected package manager. Use `{PMX}` = `npx` | `pnpm dlx` | `yarn dlx`.

| Purpose | npm | pnpm | yarn v1 | yarn Berry |
|---------|-----|------|---------|------------|
| Install | `npm install` | `pnpm install` | `yarn` | `yarn` |
| Add pkg | `npm install {pkg}` | `pnpm add {pkg}` | `yarn add {pkg}` | `yarn add {pkg}` |
| Add dev | `npm install -D {pkg}` | `pnpm add -D {pkg}` | `yarn add -D {pkg}` | `yarn add -D {pkg}` |
| Outdated | `npm outdated` | `pnpm outdated` | `yarn outdated` | `yarn upgrade-interactive` |
| Why installed | `npm explain {pkg}` | `pnpm why {pkg}` | `yarn why {pkg}` | `yarn why {pkg}` |
| Audit | `npm audit` | `pnpm audit` | `yarn audit` | `yarn npm audit` |
| Run ncu | `npx npm-check-updates` | `pnpm dlx npm-check-updates` | `yarn dlx npm-check-updates` | `yarn dlx npm-check-updates` |
| Frozen install | `npm ci` | `pnpm install --frozen-lockfile` | `yarn --frozen-lockfile` | `yarn --immutable` |
| Workspaces | `npm -ws` | `pnpm -r` | `yarn workspaces` | `yarn workspaces foreach` |

---

## Git Safety Gate (MANDATORY)

```bash
# One-command gate: checks + creates branch
node -e "const {execSync}=require('child_process');const run=(c)=>execSync(c,{stdio:['ignore','pipe','pipe']}).toString().trim();try{run('git --version')}catch(e){console.error('ERROR: git not installed');process.exit(10)}try{if(run('git rev-parse --is-inside-work-tree')!=='true')throw 0}catch(e){console.error('ERROR: not a git repo');process.exit(11)}try{if(run('git status --porcelain')){console.error('ERROR: working tree not clean');process.exit(12)}}catch(e){process.exit(13)}const d=new Date();const pad=n=>String(n).padStart(2,'0');const name='chore/upgrade-packages-'+d.getFullYear()+pad(d.getMonth()+1)+pad(d.getDate())+'-'+pad(d.getHours())+pad(d.getMinutes());try{run('git checkout -b '+name);console.log('OK: created branch '+name)}catch(e){process.exit(14)}"
```

**Rules:**
- Never run install/upgrade unless branch created
- Commit `package.json` + lockfile on this branch

---

## Package Manager Detection (MANDATORY)

```bash
node -e "const fs=require('fs'),p=require('path');const cwd=process.cwd();let pm='';try{pm=JSON.parse(fs.readFileSync(p.join(cwd,'package.json'),'utf8')).packageManager||''}catch{}const locks=[['pnpm','pnpm-lock.yaml'],['yarn','yarn.lock'],['npm','package-lock.json']].filter(([_,f])=>fs.existsSync(p.join(cwd,f)));if(pm){console.log(pm);process.exit(0);}if(locks.length===1){console.log(locks[0][0]);process.exit(0);}if(locks.length>1){console.error('ERROR: multiple lockfiles');process.exit(2);}console.error('ERROR: unable to detect PM');process.exit(1);"
```

**Priority:** `package.json.packageManager` > single lockfile > STOP

**Yarn v1 vs Berry:** Check `yarn --version` (1.x = Classic, 2+ = Berry)

---

## Pre-Upgrade Checks

### Monorepo Detection
```bash
node -e "const pkg=require('./package.json');console.log(pkg.workspaces?'MONOREPO: '+JSON.stringify(pkg.workspaces):'SINGLE_PACKAGE')"
```

### Lock File Integrity
```bash
# npm: npm ci --dry-run | pnpm: pnpm install --frozen-lockfile --dry-run | yarn: yarn --frozen-lockfile --check-files
```

### Node.js Compatibility
```bash
node -e "const pkg=require('./package.json');console.log('Engines:',pkg.engines?.node||'any','Current:',process.version)"
npm view {package}@latest engines
```

### ESM/CJS Compatibility
```bash
node -e "const pkg=require('./package.json');console.log('Type:',pkg.type||'commonjs')"
npm view {package} type exports
```

| Project | Package | Compatible | Notes |
|---------|---------|------------|-------|
| commonjs | module | Maybe | Need dynamic import() or bundler |
| module | commonjs | Yes | Node handles interop |

### Stale Package Detection
```bash
npm view {package} time.modified
```
Flag packages >2 years since last publish.

### License Check
```bash
npm view {package} license
npx license-checker --summary
```

---

## Dependency Analysis

```bash
# View tree
{PM} ls --depth=0

# Why installed
{PM} explain {pkg}  # npm
{PM} why {pkg}      # pnpm/yarn

# Peer deps
npm view {pkg} peerDependencies
```

---

## Dependency Overrides

| PM | Location | Example |
|----|----------|---------|
| npm | `overrides` | `"overrides": {"glob": "^10.0.0"}` |
| pnpm | `pnpm.overrides` | `"pnpm": {"overrides": {"glob": "^10.0.0"}}` |
| yarn | `resolutions` | `"resolutions": {"glob": "^10.0.0"}` |

---

## Breaking Change Evidence Check (MANDATORY FOR MAJOR)

### Step 1: Get metadata
```bash
npm view {pkg} repository homepage --json
```

### Step 2: Check sources
- GitHub Releases: `{repo}/releases`
- CHANGELOG: `{repo}/blob/main/CHANGELOG.md`
- Migration: `{repo}/blob/main/UPGRADING.md` or `MIGRATION.md`

### Step 3: Keywords to scan
`BREAKING`, `Removed`, `Renamed`, `Deprecated`, `Migration`, `Codemod`, `engines`, `peerDependencies`

### Step 4: Required output
```
Package: {name} | From: {current} To: {target}
Evidence: Releases: {link} | Changelog: {link} | Migration: {link} | Codemod: {cmd}
Breaking changes: {...}
Required actions: {...}
Peer constraints: {...}
Node.js requirement: {...}
```

**If no evidence found:** Proceed to Automatic Web Search (next section).

---

## Automatic Web Search for Migration Guides (MANDATORY IF NO DOCS FOUND)

When GitHub Releases, CHANGELOG, or MIGRATION docs are not found or insufficient, **automatically search the web** for migration guides before asking the user.

### Search Queries (Execute in Order)

For a package `{pkg}` upgrading from `{from}` to `{to}`:

1. **Official migration guide:**
   ```
   "{pkg}" migration guide {from} to {to}
   "{pkg}" upgrade guide v{majorFrom} to v{majorTo}
   ```

2. **Breaking changes:**
   ```
   "{pkg}" {to} breaking changes
   "{pkg}" v{majorTo} changelog breaking
   ```

3. **Codemods:**
   ```
   "{pkg}" codemod {majorTo}
   "{pkg}" migration codemod
   ```

4. **Community guides:**
   ```
   "{pkg}" upgrade tutorial {majorTo}
   "{pkg}" migration blog post
   ```

### Trusted Sources Priority

Prioritize results from these domains:
| Priority | Source | Examples |
|----------|--------|----------|
| 1 | Official docs | `{pkg}.dev`, `{pkg}.io`, `{pkg}js.org` |
| 2 | GitHub | `github.com/{org}/{pkg}` (releases, discussions, wiki) |
| 3 | Dev blogs | `dev.to`, `medium.com`, `hashnode.dev` |
| 4 | Stack Overflow | `stackoverflow.com` (tagged answers) |
| 5 | Package news | `npmjs.com`, `socket.dev` |

### Search Result Evaluation

Extract and verify:
- [ ] **Breaking changes list** - API removals, renames, behavior changes
- [ ] **Migration steps** - Ordered steps from the guide
- [ ] **Codemod availability** - Auto-fix tools (`npx {pkg}-codemod`, etc.)
- [ ] **Minimum requirements** - Node.js version, peer dependencies
- [ ] **Known issues** - Common problems and workarounds

### Decision After Web Search

| Search Result | Action |
|---------------|--------|
| Official migration guide found | Follow the guide, proceed with upgrade |
| Community guide found (verified) | Use as reference, proceed cautiously |
| Breaking changes documented | Apply changes, test thoroughly |
| Only partial info found | Combine sources, ask user to verify approach |
| No useful results | **Stop** - Ask user before proceeding |

### Example Web Search Flow

```
Package: recharts 2.x → 3.x
Step 1: Check GitHub releases → Found but sparse
Step 2: Search "recharts migration guide 2 to 3" → Found official guide
Step 3: Search "recharts 3 breaking changes" → Found API changes list
Step 4: Search "recharts codemod 3" → No codemod available
Result: Proceed with manual migration using official guide
```

### Web Search Output Template

```
Package: {pkg} | From: {from} To: {to}
Search performed: Yes
Sources found:
  - Official: {url} (migration guide)
  - GitHub: {url} (release notes)
  - Community: {url} (tutorial)
Breaking changes identified:
  - {change 1}
  - {change 2}
Migration steps:
  1. {step 1}
  2. {step 2}
Codemod: {available/not available}
Confidence: {high/medium/low}
Recommendation: {proceed/ask user/skip}
```

### If Web Search Fails

If no useful migration information is found after web search:

1. **Log the search attempt** - Document what was searched
2. **Present options to user:**
   - Proceed anyway (high risk, manual testing required)
   - Skip this package (add to Skipped Packages)
   - User provides migration link
3. **Never silently upgrade** - Always get explicit confirmation for undocumented majors

---

## Upgrade Workflow

### Order
1. Dev dependencies (lower risk)
2. Patch upgrades
3. Minor upgrades
4. Major upgrades (one at a time, with migrations)
5. Framework upgrades (Next/React) last

### For each major upgrade:
1. Locate migration docs
2. Check Node/ESM/license compatibility
3. Run codemods if available
4. Implement manual changes
5. Cleanup dead code (`npx ts-prune`, `eslint --fix`)
6. Add/update tests
7. Verify: `{PM} install && npx tsc --noEmit && npm run lint && npm test && npm run build`
8. Commit: `git commit -m "chore(deps): upgrade {pkg} {from}→{to}"`

### Time limits
| Type | Max | If exceeded |
|------|-----|-------------|
| Patch | 5min | Investigate |
| Minor | 15min | Check for hidden breaks |
| Major | 30min-1hr | Document, defer |

---

## Common Dependency Groups

| Group | Packages | Why |
|-------|----------|-----|
| React | `react`, `react-dom`, `@types/react*` | Must align |
| Next.js | `next`, `eslint-config-next` | Paired releases |
| Prisma | `prisma`, `@prisma/client` | Must match exactly |
| TypeScript | `typescript`, `@types/node` | Compatibility |
| Tailwind | `tailwindcss`, `postcss`, `autoprefixer` | Paired releases |

---

## Framework Upgrades

### Next.js
```bash
{PM_ADD} next@latest react@latest react-dom@latest
npx @next/codemod@latest {codemod-name}
```

### Prisma
```bash
{PM_ADD} prisma@latest @prisma/client@latest
npx prisma generate && npx prisma validate
```

### TypeScript
```bash
{PM_ADD} -D typescript@latest
npx tsc --noEmit
```

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| Peer conflicts | `npm view {pkg} peerDependencies`, check tree |
| ESM errors | Use dynamic `import()` or add `"type": "module"` |
| Type errors | Update `@types/{pkg}@latest` |
| Cache issues | `rm -rf .next node_modules/.cache` |
| Deprecated pkg | `npm view {pkg} deprecated`, find alternative |

### Rollback
```bash
git checkout package.json {lockfile}
{PM} install
```

---

## Pre-commit Hook Bypass (upgrade commits only)

```bash
HUSKY=0 git commit -m "chore(deps): upgrade packages"
# or: git commit --no-verify -m "..."
```

---

## Private Registry

Check `.npmrc` before upgrading. Verify auth:
```bash
npm whoami --registry={registry-url}
```

---

## Skipped Packages Template

```markdown
| Package | Current | Latest | Reason | Revisit |
|---------|---------|--------|--------|---------|
| old-lib | 2.0.0 | 3.0.0 | Needs React 19 | After React upgrade |
```

---

## Upgrade Report Template

```markdown
# Package Upgrade Report
**Date:** {date} | **PM:** {npm|pnpm|yarn} | **Branch:** {branch}

## Summary
- Upgraded: {count} | Major: {count} | Minor: {count} | Patch: {count} | Skipped: {count}

## Upgrades
| Package | From | To | Breaking | Migration |
|---------|------|----|---------:|-----------|

## Breaking Changes
### {pkg} {from}→{to}
- Docs: {link}
- Codemods: {cmd}
- Manual changes: {...}
- Tests added: {...}

## Verification
- [ ] Type check | [ ] Lint | [ ] Tests | [ ] Build

## Skipped
| Package | Reason |

## Rollback
`git checkout main -- package.json {lockfile} && {PM} install`
```

---

## Automated PR Creation

```bash
git push -u origin $(git branch --show-current)
gh pr create --title "chore(deps): upgrade packages $(date +%Y-%m-%d)" --body "## Package Upgrade\nSee upgrade report for details."
```

---

## Semver Notes

- **Pre-1.0.0:** Treat minors as potential breaking changes
- **@types/*:** Often break in patches; pin exact or test carefully
- **Prereleases (alpha/beta/rc/canary):** Never in production deps, only devDependencies with explicit justification

---

## Decision Matrix

| Situation | Action |
|-----------|--------|
| No errors | Continue |
| Minor type errors | Fix and continue |
| Major API changes | Follow migration + codemods + tests |
| No docs found | **Web search first**, then ask user if nothing found |
| Tests fail | Fix or revert |
| Time exceeded | Document, defer, create issue |
| ESM/CJS incompatible | Check bundler or find alternative |
| Node incompatible | Defer or find alternative |

---

## Detecting Major Version Blockers

Before upgrading any major version, run these checks to detect potential blockers.

### Step 1: Check Your Project Module Type

```bash
node -e "const pkg=require('./package.json'); console.log('Project type:', pkg.type || 'commonjs')"
```

| Result | Meaning |
|--------|---------|
| `commonjs` | Default Node.js, may have issues with ESM-only packages |
| `module` | ESM project, compatible with all modern packages |

### Step 2: Check if Target Package is ESM-Only

```bash
# Check the target version's module type
npm view {package}@{version} type

# Returns "module" = ESM-only
# Returns nothing/undefined = CommonJS compatible
```

### Step 3: ESM/CJS Compatibility Decision

| Your Project | Package Type | Compatible? | Action |
|--------------|--------------|-------------|--------|
| `commonjs` | `module` (ESM-only) | ❌ No | Skip, find alternative, or migrate project to ESM |
| `commonjs` | (empty/commonjs) | ✅ Yes | Safe to upgrade |
| `module` | `module` (ESM-only) | ✅ Yes | Safe to upgrade |
| `module` | (empty/commonjs) | ✅ Yes | Safe to upgrade |

### Step 4: Check Node.js Engine Compatibility

```bash
# Check required Node.js version
npm view {package}@{version} engines

# Compare with your project's Node version
node --version
```

If package requires newer Node.js than your project supports, skip or upgrade Node.js first.

### Step 5: Check for Deprecation

```bash
# Check if package is deprecated
npm view {package} deprecated

# If deprecated, find alternative
npm search {alternative-keywords}
```

### Step 6: Check Peer Dependencies

```bash
# View peer dependency requirements
npm view {package}@{version} peerDependencies

# Ensure your installed versions satisfy requirements
```

### Quick Pre-Upgrade Script

Run this before any major upgrade:

```bash
# Replace {pkg} and {ver} with actual values
node -e "
const pkg='{pkg}', ver='{ver}';
const {execSync}=require('child_process');
const run=c=>{try{return execSync(c,{encoding:'utf8'}).trim()}catch{return''}};
const projType=require('./package.json').type||'commonjs';
const pkgType=run('npm view '+pkg+'@'+ver+' type');
const engines=run('npm view '+pkg+'@'+ver+' engines');
const deprecated=run('npm view '+pkg+' deprecated');
console.log('Package:', pkg+'@'+ver);
console.log('Project type:', projType);
console.log('Package type:', pkgType||'commonjs');
console.log('Engines:', engines||'any');
console.log('Deprecated:', deprecated||'no');
console.log('---');
if(projType==='commonjs'&&pkgType==='module'){
  console.log('⚠️  BLOCKER: ESM-only package in CommonJS project');
  console.log('Options: 1) Skip upgrade  2) Find alternative  3) Migrate project to ESM');
}else if(deprecated){
  console.log('⚠️  BLOCKER: Package is deprecated');
}else{
  console.log('✅ No blockers detected - verify build after upgrade');
}
"
```

### CommonJS Project with ESM-Only Package: Options

If you encounter an ESM-only package in a CommonJS project:

1. **Skip upgrade** - Stay on last CommonJS-compatible version
2. **Find alternative** - Search for CJS-compatible package with similar functionality
3. **Use dynamic import** - `const pkg = await import('{package}')` (async only)
4. **Use bundler** - Webpack/Vite can sometimes handle ESM in CJS projects
5. **Migrate to ESM** - Add `"type": "module"` to package.json (major change)

Overview

This skill safely upgrades JavaScript and TypeScript project dependencies with breaking-change detection, migration guidance, and automated code migrations across npm, pnpm, and yarn. It enforces git safety by requiring a clean working tree and a new branch, detects package manager and monorepo layout, and runs stepwise upgrades to minimize risk. The skill produces actionable evidence for major upgrades and either applies codemods or prompts for manual migration steps.

How this skill works

It first verifies the repository is a JavaScript/TypeScript project and detects the package manager and lockfile to avoid mixing tools. It enforces a git safety gate (clean tree + new branch) and runs pre-upgrade checks: lockfile integrity, Node engine, ESM/CJS, peer deps, and stale package age. For each candidate upgrade it analyzes dependency trees, searches for breaking-change evidence (releases, changelogs, migration guides, codemods) and either applies automated codemods or returns a migration plan and tests to run. Majors are handled one at a time with time-boxing, tests, and mandatory documentation of skips.

When to use it

  • You need to upgrade dependencies in a JS/TS (Node.js) project
  • You want to minimize risk for major version upgrades with migration guidance
  • You require git safety guarantees and reproducible lockfile commits
  • You need automated codemods or concrete manual migration steps
  • You maintain a monorepo and want workspace-aware upgrade sequencing

Best practices

  • Always run the project language detection first and stop if not JS/TS
  • Never mix package managers; enforce single detected PM and lockfile integrity
  • Create a new branch before any install/upgrade and commit package.json + lockfile changes
  • Upgrade incrementally: dev deps → patches → minors → majors (one major at a time)
  • For major upgrades locate migration docs or perform an automatic web search before proceeding; if none found, ask the user
  • Run build, typecheck, lint, and tests after each major upgrade and keep changes time-boxed

Example use cases

  • Upgrade a single library (e.g., react 17→18) with migration guide and codemod application
  • Bump multiple minor/patch dependencies across a workspace while preserving lockfile integrity
  • Handle a framework upgrade (Next.js) with codemods, test updates, and paired package alignment
  • Detect an incompatible peer dependency and propose an override or skip with documented rationale
  • Audit old/stale packages (not updated >2 years) and either replace or flag them for manual review

FAQ

What if the repository is not a JavaScript/TypeScript project?

The skill stops immediately and reports the detected project file with an alternative command for that language; do not proceed.

How are breaking major upgrades handled?

Majors are upgraded one at a time only after finding migration docs or running an automatic web search; codemods are applied when available, otherwise a manual migration plan is produced and user confirmation is required.