Stephen

Hackett-Delaney

Full Stack Software Engineer

ArticlesAgentic Workflows

Automated Package Upgrades

workflowsautomationclaude-codepackage-managementgithub-cli

February 7, 2026

This is Part 2 of my Agentic Workflows series. Part 1 covers the foundational pattern of separating workflows from core documentation.


I recently upgraded my portfolio site from React 18 to 19, Next.js 14 to 16, Tailwind 3 to 4, and a dozen other packages—all in a single session with Claude Code. Here's the workflow that made it manageable.

The Challenge

Running pnpm outdated showed 15+ packages needing updates. Some were patches, some were minors, and some were majors with breaking changes. The naive approach—update everything and pray—doesn't scale.

The Tiered Strategy

Not all updates are equal. I categorized them into three tiers:

TierCriteriaRiskAction
Tier 1Patches (x.y.Z), @types/*LowBatch into single PR
Tier 2Minors (x.Y.z)MediumIndividual PRs
Tier 3Majors (X.y.z)HighIssue first, then PR

This gives you:

  • Speed for low-risk changes (batch them)
  • Isolation for medium-risk (easy rollback)
  • Review for high-risk (approval before implementation)

The Workflow

Here's the complete flow I use:

Step 1: Discovery

pnpm outdated

Output shows current vs latest versions. Categorize each into tiers.

Step 2: Dependency Chains

Some packages must be upgraded in order:

React 18 → React 19
         ↓
    Next.js 16
         ↓
eslint-config-next 16

Upgrading Next.js before React would fail. Map these chains first.

My upgrade order for this stack:

  1. React (unlocks Next.js)
  2. Next.js (unlocks eslint-config-next)
  3. TypeScript (unlocks @typescript-eslint)
  4. ESLint
  5. Tailwind CSS
  6. Everything else

Step 3: For Each Package

3a. Research

Search for the changelog or migration guide:

# Or ask Claude to web search
"Find the Next.js 16 migration guide"

Look for:

  • Breaking changes
  • New runtime requirements (Node version!)
  • Deprecated APIs
  • Required code changes

3b. Create Issue (Tier 2-3)

gh issue create \
  --title "🔴 Major: Upgrade Next.js 14 → 16" \
  --body "## Breaking Changes
- Turbopack now default
- images.domains deprecated
- Node 20 required

## Affected Files
- next.config.js
- .github/workflows/pr-open.yml"

This creates traceability—every upgrade links to research.

3c. Branch and Implement

git checkout -b chore/upgrade-nextjs-16
pnpm add next@latest

Fix any breaking changes found in research.

3d. Verify Locally

This is critical:

pnpm exec tsc --noEmit   # Type check
pnpm lint                 # Lint check
pnpm build-local          # Full build ← DON'T SKIP

Lesson learned: pnpm dev doesn't catch everything. I missed a Turbopack error that only showed in production builds. Always run build-local before pushing.

3e. PR and Merge

git add -A && git commit -m "chore: upgrade Next.js 14 → 16"
git push -u origin chore/upgrade-nextjs-16

gh pr create --title "chore: upgrade Next.js 14 → 16" \
  --body "Closes #63"

gh pr checks 69 --watch    # Wait for CI
gh pr merge 69 --squash --delete-branch

3f. Document

Create a changelog entry:

cp knowledge/upgrades/_template.md knowledge/upgrades/nextjs-16.md

Fill in what changed, migration notes, and gotchas for future reference.

Real Examples from My Session

Next.js 14 → 16: Turbopack Surprises

The gotcha: Turbopack (now default) requires static config exports.

// ❌ Broke - dynamic import
import { mediaHandlerConfig } from './config'
export const config = mediaHandlerConfig

// ✅ Fixed - static literal
export const config = {
  api: { bodyParser: false }
}

Also hit Node version requirements—CI was on Node 18, Next.js 16 needs Node 20. Easy fix once identified, but would've been caught earlier with the research step.

Tailwind 3 → 4: CSS-First Config

Tailwind 4 changes everything about configuration:

/* Old: tailwind.config.ts */
/* New: CSS-first in globals.css */
@import 'tailwindcss';
@config '../tailwind.config.mjs';

The official upgrade tool (npx @tailwindcss/upgrade) handled most of it, but my custom desktop screen (for hover-capable devices) needed manual migration:

/* Custom variant for hover-capable devices */
@custom-variant desktop (&:is(@media (hover: hover) and (pointer: fine) *));

ESLint 8 → 9: Flat Config Migration

ESLint 9 uses "flat config" instead of .eslintrc.json:

// eslint.config.mjs
import { defineConfig, globalIgnores } from "eslint/config";
import next from "eslint-config-next";

export default defineConfig([
  ...next,
  globalIgnores([".next/**", "out/**"]),
  { rules: { /* ... */ } }
]);

ESLint 10 is blocked—plugins don't support it yet. Documented and moved on.

The Knowledge Base

Every upgrade gets documented in the knowledge base:

knowledge/
└── upgrades/
    ├── _template.md          # Copy this
    ├── nextjs-16.md          # Turbopack gotchas
    ├── tailwind-4.md         # CSS-first migration
    ├── jest-eslint.md        # Flat config details
    └── README.md             # Index

Each file includes:

  • Date, Issue link, PR link
  • What changed
  • Migration notes and gotchas
  • Files modified

This is gold for future upgrades. When Next.js 17 drops, I'll check what we learned from 16.

GitHub CLI Automation

The workflow leans heavily on gh:

# Create issue with research
gh issue create --title "..." --body "..."

# Create PR linked to issue
gh pr create --title "..." --body "Closes #N"

# Watch CI (blocks until done)
gh pr checks 69 --watch

# Merge when green
gh pr merge 69 --squash --delete-branch

No context switching to the browser. Everything stays in the terminal.

Results

In one session:

  • 10 PRs merged (#65-74)
  • 9 packages upgraded (including 4 majors)
  • 9 knowledge entries created
  • 0 production issues (so far)

The tiered approach meant I could batch the boring stuff and focus attention on the risky upgrades.

The Workflow File

The complete workflow lives at /workflows/package-audit.md. It includes:

  • Full step-by-step process
  • Issue/PR templates
  • Common issues and fixes
  • Turbopack gotchas

When I ask Claude to "audit packages," it reads this file and executes the workflow. No re-explaining the process each time.

What's Next

Part 3 will cover the article-builder workflow—how I turned this coding session into the blog post you're reading. Yes, it's recursive.


Built with Claude Code using the workflow pattern from Part 1.

© 2026. All rights reserved