Audit and reduce dependencies
Reduce JavaScript dependency footprint. Use pnpm only. Preserve the lockfile, workspace layout, and dependency range style unless there is a concrete reason to change them.
For GitHub Actions workflow triage (action choice, permissions, pinning), use a dedicated workflow audit — not the reporting format below (workflow file + step only when the finding is pnpm install policy).
Workflow
- Hardening gate: run
/check-npm (read-only). See check-npm.
- Establish the baseline.
- Remove unused direct dependencies.
- Deduplicate direct dependency versions in monorepos.
- Rank direct dependencies by transitive lockfile closure.
- Use closure data to find low-risk minor/patch upgrades.
- Use closure data to find trivial dependencies worth inlining.
- Check e18e recommendations for replacements/removals.
- Reinstall, verify, and report measured impact.
Step 0: Hardening gate (/check-npm)
Run /check-npm before mutating manifests or lockfiles.
- If any check FAILs: report the table and fix snippets; do not weaken
pnpm-workspace.yaml, .npmrc, CI install flags, or Renovate age gates during cleanup.
- Do not paste full hardening config into this workflow —
/check-npm owns version thresholds, script policy, git-dep protocols, and min release age.
- If the user only asked for hardening (not reduction), stop after
/check-npm unless they also want cleanup.
pnpm 11+: script and release-age policy live in pnpm-workspace.yaml, not .npmrc or package.json#pnpm (pnpm 11 no longer reads the package.json#pnpm field). Verify each key against the installed pnpm major before suggesting config. Never add unsupported keys. Do not lower an existing minimumReleaseAge (or org equivalent) during cleanup.
Dependency triage
For each non-trivial direct dependency (especially after Steps 4–7), assign one label:
Replacements and new direct deps
- No new direct dependencies (including swaps) without explicit user approval.
- Prefer Remove (inline/native APIs) over Replace-with-Better when equivalent.
- For Replace-with-Better: state why (maintenance, security, smaller tree); prefer actively maintained, widely adopted packages from trusted maintainers.
- Respect repo
minimumReleaseAge / Renovate gates; command-level 72h freshness is a floor, not permission to bypass stricter config.
pnpm & supply-chain
Confirm the repo uses pnpm: pnpm-lock.yaml, pnpm-workspace.yaml, and/or packageManager / devEngines.packageManager.name set to pnpm in root package.json. If not on pnpm, stop — do not migrate package managers as part of cleanup.
Respect repo install policy when present (e.g. pnpm install --frozen-lockfile --ignore-scripts).
Lifecycle scripts: Always --ignore-scripts on pnpm install, pnpm add, and pnpm remove unless the user explicitly writes allow scripts in the same message (state which scripts would run and the risk). For pnpm dlx, dlx does not accept --ignore-scripts directly — use pnpm --config.ignore-scripts=true dlx (flags after dlx are forwarded to the executed binary). If a dependency legitimately needs a build script (native modules, etc.), finish without scripts, then ask whether to run a specific manual rebuild (e.g. pnpm rebuild <pkg>).
Freshness check (≥ 72 hours) — required before any command that adds or upgrades a named package version (pnpm add, pnpm dlx with new/upgraded direct version). Not required for plain pnpm install / pnpm remove with no new package argument.
For each directly named package:
curl -s https://registry.npmjs.org/<package-name>
- Resolve version: pinned
pkg@1.2.3 → that version; range/latest/unspecified → dist-tags.latest
- Read
time["<version>"]
- If published less than 72 hours ago → stop. Tell the user package, version, and exact age. Suggest an older known-good pin unless they write override freshness check.
@grafana/* scoped packages are exempt from the freshness check; --ignore-scripts still applies.
After a failed freshness check, do not substitute a different version without user approval.
Safety rules
- Work in small batches so lockfile diffs remain reviewable.
- Never trust unused-dependency tools blindly; verify imports, config files, scripts, generated code hooks, framework conventions, plugin names, CLIs, and dynamic imports.
- You may write scripting and parsing to verify
package.json and lockfile dependency accounts.
- Treat
peerDependencies, optionalDependencies, package bin usage, test fixtures, and published package manifests as higher risk.
- Do not remove or inline dependencies used for security, parsing, crypto, Unicode, URL handling, date/time, i18n, or platform compatibility unless the replacement is proven equivalent.
- Do not switch package managers, delete
pnpm-lock.yaml, or rewrite workspace structure as part of cleanup.
- Treat
pnpm dedupe as potentially behavior-changing; inspect lockfile diffs and run focused verification before keeping the result.
- Measure before and after: direct dependency count, lockfile line count or entry count, package count, and estimated
node_modules size when available.
Step 1: Baseline
Collect:
- All
package.json files and workspace boundaries (pnpm-workspace.yaml).
pnpm-lock.yaml, pnpm-workspace.yaml security settings (minimumReleaseAge, strictDepBuilds, blockExoticSubdeps, allowBuilds), and install policy in .npmrc / CI flags (e.g. --frozen-lockfile, --ignore-scripts).
- Direct dependency names by manifest section:
dependencies, devDependencies, peerDependencies, optionalDependencies.
- Existing verification commands from scripts, CI, or repo docs.
- CI spot-check (
.github/workflows or equivalent): installs should use pnpm install --frozen-lockfile and script blocking consistent with workspace config. Flag workflows that regenerate lockfiles on every run.
- Renovate / Dependabot (if present): note
minimumReleaseAge for npm packages; do not reduce it during cleanup.
Record baseline metrics: git status --short, wc -l pnpm-lock.yaml. If node_modules is installed, estimate footprint with platform-appropriate tools. Lockfile reductions are the primary metric — do not depend on node_modules being present.
Step 2: Remove unused direct dependencies
Unsafe direct dependency protocols — scan all workspace package.json dependency sections. Flag values that are not: semver range, workspace:, patch:, or npm: alias to semver. Flag git: / github: / tarball URLs / user/repo shorthand / file: / link: / exec: / etc. (same allow-list as /check-npm). Do not remove flagged entries silently; report for a separate hardening PR unless the user asked to fix them.
Use a static analyzer as a starting point, not as proof (knip, depcheck, or repo-native tooling). Run with pinned pnpm --config.ignore-scripts=true dlx <tool>@<version> <args...> when not installed (freshness-check the pin first).
For each candidate:
- Search code, configs, package scripts, build tooling, tests, and docs for the package name and known import paths.
- Check whether required by a published package manifest, peer contract, plugin loader, CLI command, or dynamic
require/import.
- Remove only when no real usage remains.
- Run
pnpm install --ignore-scripts (+ repo flags) and focused verification.
If usage is only in a script or config, consider moving between dependencies and devDependencies instead of removing.