Skill: Package Upgrade (Cross-Platform)
Safely upgrade JavaScript packages while minimizing breaking change risk, with migration guidance and automated code upgrades.
Non-Negotiables
- Verify JavaScript project first - Stop immediately if not a JS/Node.js project
- Never force upgrade all at once - Upgrade incrementally
- Detect package manager first - npm/pnpm/yarn (including version)
- Never mix package managers - Refuse if lockfiles conflict
- Git safety gate first - Clean repo + new branch before upgrades
- Breaking-change evidence check - For majors, check Releases/CHANGELOG/Migration/Codemods
- For breaking upgrades: migrate code + tests - Apply codemods, implement fixes, add tests
- Check peer dependencies - Ensure compatibility before upgrading
- Check Node.js engine compatibility - Verify target versions support project's Node
- Check ESM/CJS compatibility - Verify module system compatibility
- Test after each major upgrade - Run build and tests
- Keep lockfile updated - Commit lockfile changes
- Document skipped packages - Record why packages were not upgraded
- 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 {pa