Agent Skills: Skill: Package Upgrade (Cross-Platform)

Safely upgrade JavaScript packages with breaking change detection, migration guidance, and automated code migrations (npm/pnpm/yarn). Cross-platform with git safety branch enforcement.

UncategorizedID: georgekhananaev/claude-skills-vault/upgrade-packages-js

Install this agent skill to your local

pnpm dlx add-skill https://github.com/georgekhananaev/claude-skills-vault/tree/HEAD/.claude/skills/upgrade-packages-js

Skill Files

Browse the full folder contents for upgrade-packages-js.

Download Skill

Loading file tree…

.claude/skills/upgrade-packages-js/SKILL.md

Skill Metadata

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.

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.

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)

# 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)

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

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

Lock File Integrity

# npm: npm ci --dry-run | pnpm: pnpm install --frozen-lockfile --dry-run | yarn: yarn --frozen-lockfile --check-files

Node.js Compatibility

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

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

npm view {package} time.modified

Flag packages >2 years since last publish.

License Check

npm view {package} license
npx license-checker --summary

Dependency Analysis

# 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

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

{PM_ADD} next@latest react@latest react-dom@latest
npx @next/codemod@latest {codemod-name}

Prisma

{PM_ADD} prisma@latest @prisma/client@latest
npx prisma generate && npx prisma validate

TypeScript

{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

git checkout package.json {lockfile}
{PM} install

Pre-commit Hook Bypass (upgrade commits only)

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

Private Registry

Check .npmrc before upgrading. Verify auth:

npm whoami --registry={registry-url}

Skipped Packages Template

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

Upgrade Report Template

# 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

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

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

# 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

# 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

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

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

Step 6: Check Peer Dependencies

# 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:

# 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)