| name | deps-audit |
| description | Audit dependencies for vulnerabilities, staleness, unused packages, and license risks — produce a health report with actionable fixes |
| argument-hint | [--fix] (optional — auto-fix safe updates) |
| allowed-tools | Bash, Read, Write, Edit, Glob, Grep, Agent |
/deps-audit — Dependency Health Audit
Audit the project's dependency tree for security vulnerabilities, outdated packages, unused dependencies, and license compliance. Produce a structured report and optionally auto-fix safe updates.
Arguments
$ARGUMENTS may contain --fix to auto-apply safe updates (patch/minor only)
Phase 0 — Pre-flight
- Confirm we're in the codegraph repo root (check for
package.json and package-lock.json)
- Run
node --version — must be >= 20
- Run
npm --version to capture toolchain info
- Parse
$ARGUMENTS — set AUTO_FIX=true if --fix is present
- If
AUTO_FIX is set: Save the original manifests now, before any npm commands run, so pre-existing unstaged changes are preserved:
git stash push -m "deps-audit-backup" -- package.json package-lock.json
STASH_REF=$(git stash list --format='%gd %s' | grep 'deps-audit-backup' | head -1 | awk '{print $1}')
STASH_REF is non-empty if and only if a stash entry was actually created. Do not use $? — modern git (2.16+) returns 0 even when nothing was stashed.
Use [ -n "$STASH_REF" ] (stash created) / [ -z "$STASH_REF" ] (nothing stashed) for all branching. Use $STASH_REF (not stash@{0}) in all later stash drop/pop commands to avoid targeting the wrong entry.
Phase 1 — Security Vulnerabilities
Run npm audit --json and parse the output:
- Count vulnerabilities by severity:
critical, high, moderate, low, info
- For each
critical or high vulnerability:
- Record: package name, severity, CVE/GHSA ID, vulnerable version range, patched version, dependency path (direct vs transitive)
- Check if a fix is available (
npm audit fix --dry-run --json)
- Summarize: total vulns, fixable count, breaking-fix count
If AUTO_FIX is set: Run npm audit fix (non-breaking fixes only). Record what changed. Do NOT run npm audit fix --force — breaking changes require manual review.
Phase 2 — Outdated Dependencies
Run npm outdated --json and categorize:
2a. Direct dependencies (dependencies + devDependencies)
For each outdated package, record:
- Package name
- Current version → Wanted (semver-compatible) → Latest
- Whether the update is patch, minor, or major
- If major: check the package's CHANGELOG/release notes for breaking changes relevant to our usage
2b. Staleness score
Classify each outdated dep:
| Category | Definition |
|---|
| Fresh | On latest or within 1 patch |
| Aging | 1+ minor versions behind |
| Stale | 1+ major versions behind |
| Abandoned | No release in 12+ months (check npm registry publish date) |
For any package classified as Abandoned, check if there's a maintained fork or alternative.
If AUTO_FIX is set: Run npm update to apply semver-compatible updates. Record what changed.
Phase 3 — Unused Dependencies
Detect dependencies declared in package.json but never imported:
- Read
dependencies and devDependencies from package.json
- For each dependency, search for imports/requires across
src/, tests/, scripts/, mcp/, graph/, ast-analysis/, cli.js, index.js:
require('<pkg>') or require('<pkg>/...')
import ... from '<pkg>' or import '<pkg>'
import('<pkg>') (dynamic imports)
- Skip known implicit dependencies that don't have direct imports:
@anthropic-ai/tokenizer — peer dependency of @anthropic-ai/sdk; the SDK may require it at runtime without an explicit import in our code (verify against package.json before removing)
tree-sitter-* and web-tree-sitter — loaded dynamically via WASM
@biomejs/biome — used as CLI tool only
commit-and-tag-version — used as npm script
@optave/codegraph-* — platform-specific optional binaries
vitest — test runner, invoked via CLI
- Anything in
optionalDependencies
- For each truly unused dep: recommend removal with
npm uninstall <pkg>
Important: Some deps are used transitively or via CLI — don't blindly remove. Flag as "likely unused" and let the user decide.
Phase 4 — License Compliance
Check licenses for all direct dependencies:
- For each package in
dependencies, read its node_modules/<pkg>/package.json → license field
- Classify:
- Permissive (MIT, ISC, BSD-2-Clause, BSD-3-Clause, Apache-2.0, 0BSD, Unlicense): OK
- Weak copyleft (LGPL-2.1, LGPL-3.0, MPL-2.0): Flag for review
- Strong copyleft (GPL-2.0, GPL-3.0, AGPL-3.0): Flag as risk — may conflict with MIT license of codegraph
- Unknown/UNLICENSED/missing: Flag for investigation
- Only flag non-permissive licenses — don't list every MIT dep
Phase 5 — Duplicate Packages
Check for duplicate versions of the same package in the dependency tree:
- Run
npm ls --all --json and look for packages that appear multiple times with different versions
- Only flag duplicates that add significant bundle weight (> 100KB) or are security-sensitive (crypto, auth, etc.)
- Suggest deduplication:
npm dedupe
Phase 6 — Report
Write a report to generated/deps-audit/DEPS_AUDIT_<date>.md with this structure:
# Dependency Audit Report — <date>
## Summary
| Metric | Value |
|--------|-------|
| Total dependencies (direct) | N |
| Total dependencies (transitive) | N |
| Security vulnerabilities | N critical, N high, N moderate, N low |
| Outdated packages | N stale, N aging, N fresh |
| Unused dependencies | N |
| License risks | N |
| Duplicates | N |
| **Health score** | **X/100** |
## Health Score Calculation
- Start at 100
- -20 per critical vuln, -10 per high vuln, -3 per moderate vuln
- -5 per stale (major behind) dep, -2 per aging dep
- -5 per unused dep
- -10 per copyleft license risk
- Floor at 0
## Security Vulnerabilities
<!-- Detail each critical/high vuln with remediation -->
## Outdated Packages
<!-- Table: package, current, latest, category, notes -->
## Unused Dependencies
<!-- List with evidence (no imports found) -->
## License Flags
<!-- Only non-permissive licenses -->
## Duplicates
<!-- Only significant ones -->
## Recommended Actions
<!-- Prioritized list: fix vulns > remove unused > update stale > dedupe -->
Phase 7 — Auto-fix Summary (if --fix)
If AUTO_FIX was set:
Summarize all changes made:
- List each package updated/fixed
- Run
npm test to verify nothing broke
- If tests pass and
STASH_REF is non-empty: pop and merge the saved state (git stash pop $STASH_REF) — this restores any pre-existing uncommitted changes alongside the npm fix results. Note: the step 2 test run validated the npm changes alone; step 3b below is the authoritative test of the final merged state.
- If the pop applies cleanly:
a. Run
npm install to re-sync node_modules/ with the merged manifest.
b. Re-run npm test to confirm the merged state is consistent (this is the authoritative check — step 2 only validated the npm changes in isolation).
c. If tests still pass: confirm the project is consistent.
d. If tests now fail: warn the user — the pre-existing manifest changes conflict with the audit fixes.
Recovery options:
- To undo all manifest changes (both audit fixes and pre-existing):
git checkout -- package.json package-lock.json && npm ci
- To keep only the audit fixes and discard pre-existing changes: manually edit
package.json/package-lock.json to remove the pre-existing delta, then npm ci
- To keep only the pre-existing changes and discard the audit fixes:
git checkout HEAD -- package.json package-lock.json && npm ci to revert manifests to their clean state, then manually re-apply only your pre-existing changes
- If the pop causes conflicts in
package.json/package-lock.json: warn the user, leave conflict markers for manual resolution, and instruct: "After you resolve the conflicts, run npm install to re-sync node_modules/ with the resolved lock file before committing."
- For conflicts in other files, resolve them by keeping both the npm fixes and the pre-existing changes.
If tests pass and
STASH_REF is empty: no action needed — the npm changes are good and no stash entry exists to clean up
- If tests fail and
STASH_REF is non-empty:
- Reset manifests to HEAD first (undoes npm changes):
git checkout HEAD -- package.json package-lock.json
- Then re-apply the pre-existing changes cleanly:
git stash pop $STASH_REF
- Restore
node_modules/ to match the reverted lock file: npm ci
- Report what failed
- If tests fail and
STASH_REF is empty:
- Discard manifest changes:
git checkout -- package.json package-lock.json
- Restore
node_modules/ to match the reverted lock file: npm ci
- Report what failed
Rules
- Never run
npm audit fix --force — breaking changes need human review
- Never remove a dependency without asking the user, even if it appears unused — flag it in the report instead
- Always run tests after any auto-fix changes
- If
--fix causes test failures, first reset manifests to HEAD (git checkout HEAD -- package.json package-lock.json), then re-apply pre-existing changes (git stash pop $STASH_REF if STASH_REF is non-empty, or no-op if nothing was stashed), then run npm ci to resync node_modules/, and report the failure
- Treat
optionalDependencies separately — they're expected to fail on some platforms
- The report goes in
generated/deps-audit/ — create the directory if it doesn't exist