Data-driven deep evaluation of whether a dependency is worth removing
Deep-dive evaluation of whether $ARGUMENTS is worth removing, using a 5-phase data-driven framework. This command picks up where uzomuzo diet leaves off -- diet gives you the priority ranking, this gives you the removal verdict.
When to use: After uzomuzo diet ranks a dependency and you need to decide whether to invest the effort. Works for any difficulty level -- trivial deps get a quick confirmation, hard deps get a concrete migration plan.
Relationship to other skills:
uzomuzo diet tells you ranking and difficulty (automated, all dependencies at once)/diet-evaluate-removal tells you whether it's worth removing and how (LLM-powered, one dependency at a time)/diet-assess-risk tells you how dangerous it is to keep (LLM-powered, security focus)/diet-remove executes the removal (LLM-powered, implementation)Extract the target dependency's diet JSON entry. Prefer existing JSON over re-running diet.
Option A -- existing diet JSON (preferred):
jq '.dependencies[] | select(.name == "$ARGUMENTS")' diet.json
Option B -- run diet now:
uzomuzo diet --sbom bom.json --source . --format json | jq '.dependencies[] | select(.name == "$ARGUMENTS")'
Display ALL of these from the diet JSON. Do NOT re-compute any of them.
| Field | What it tells you |
|---|---|
name / version / ecosystem | Dependency identity |
purl | Canonical package URL identifier |
scope | Dependency scope: "tool" (build-time-only), "runtime" (reflection/ServiceLoader-loaded) |
rank / priority_score | Where this dep sits in the removal priority list |
difficulty | trivial / easy / moderate / hard |
exclusive_transitive / total_transitive | Deps removed together / total transitive count |
stays_as_indirect | Whether it remains after removing direct usage |
indirect_via | Which other deps pull it in transitively |
import_file_count | Number of files importing this dep |
call_site_count | Total call sites across all files |
api_breadth | Number of distinct APIs used |
symbols | Exact API surface used (function/type names) |
import_files | Exact file paths that import this dep |
has_blank_import / has_dot_import / has_wildcard_import | Special import patterns |
is_unused | Whether diet detected zero imports |
lifecycle | Active / Stalled / Legacy-Safe / EOL-Confirmed / EOL-Effective / EOL-Scheduled / Archived / Review Needed |
has_vulnerabilities / vulnerability_count / max_cvss_score | Security signals |
overall_score | Composite health score (OpenSSF Scorecard) |
graph_impact / coupling_effort / health_risk | Score components |
Before proceeding, flag any of these patterns -- they indicate diet's coupling data may be unreliable:
scope: "tool" + import_file_count: 0 -- Expected for build-time-only tool deps. Not an IBNC pattern. These have zero runtime imports by design.scope: "runtime" + import_file_count: 0 -- Expected for runtime-mechanism deps (JDBC drivers, logging backends, Spring auto-config). Loaded via reflection/ServiceLoader/classpath, not static imports.has_blank_import: true + call_site_count: 0 -- The blank import IS the usage (Go DB drivers, JS polyfills). Coupling is underestimated.has_wildcard_import: true -- All symbols are in scope. api_breadth may undercount.has_dot_import: true -- Broadly coupled; symbols lack package qualifier.import_file_count > 0 + call_site_count: 0 (without blank/dot/wildcard flags) -- Possible IBNC pattern (type-only usage, annotation processing, framework DI, config-driven plugins). Investigate before trusting "0 calls." Note: distinct from the blank-import pattern above -- blank imports typically produce call_site_count = 1, not 0.Classify each entry in import_files by its role in the project. This is the single most important step -- it determines the effective difficulty of removal.
| Category | Path patterns | Impact |
|---|---|---|
| Production | src/, lib/, internal/, pkg/, cmd/, app/ (not under test/) | Full migration required |
| Test | *_test.go, test/, tests/, __tests__/, spec/, *.test.ts | Can delete or swap independently |
| CI / Infrastructure | .github/, scripts/, ci/, Makefile, build/ | Often trivially replaceable |
| Example / Demo | examples/, example/, demo/, sample/ | Can delete or update separately |
| Generated | Files with // Code generated header, *_gen.go, *.pb.go | Re-run generator with replacement |
| Vendored | vendor/, node_modules/, third_party/ | Exclude from analysis -- copies of the dep, not usage |
Usage Classification for {name}:
Production: {N} files [{file list}]
Test: {N} files [{file list}]
CI/Infra: {N} files [{file list}]
Example: {N} files [{file list}]
Generated: {N} files [{file list}]
Effective scope: {N} production files (out of {total} total)
| diet difficulty | All files are test/CI/example | Adjusted difficulty |
|---|---|---|
| hard | Yes | easy (no production impact) |
| moderate | Yes | trivial |
| easy/trivial | -- | unchanged |
If ALL import files are non-production, note: "Zero production code impact."
Two checks that diet cannot automate: API leakage (a binary gate) and symbol replaceability (requires domain knowledge).
Critical question: Do any types from this dependency appear in EXPORTED identifiers?
Look for the package's types in exported function signatures (parameter types, return types), exported struct field types, and exported interface method signatures. Internal usage within function bodies is NOT API leakage.
Check approach by ecosystem:
| Ecosystem | What to check |
|---|---|
| Go | Exported func signatures, struct fields, and interface methods that reference the dep's types. Search non-test files for the dep's package name in type positions. |
| npm/TS | export ... from '{pkg}' re-exports, or exported types/interfaces using the dep's types. |
| Python | Re-exports in __init__.py files: from {pkg} import ... that are part of the public API. |
| Java/Maven | Public class signatures, method parameters, return types, or field types referencing the dep's packages in non-test source sets. |
Using the symbols list from diet, classify each symbol's replaceability. This is where the LLM adds the most value.
Symbol Migration Map for {name}:
| Symbol | Category | Replacement | Notes |
|--------|----------|-------------|-------|
| {sym1} | stdlib | {specific function} | Available since {version} |
| {sym2} | existing-dep | {dep already in project} | Already in go.mod/package.json |
| {sym3} | self-impl | {N}-line implementation | Non-crypto, well-defined |
| {sym4} | no-replacement | -- | Crypto/protocol, keep or find alternative lib |
Summary: {N}/{total} stdlib, {N} existing-dep, {N} self-impl, {N} no-replacement.
Rules for symbol classification:
os.UserHomeDir(), slices.SortFunc()). Note the minimum language version required.Special cases:
is_unused: true -- skip this step. The dep can simply be deleted.has_blank_import: true and call_site_count <= 1 -- the import itself is the usage (driver registration, polyfill). Check what init() does; the replacement is typically a different driver package, not a code change. Score Replaceability as High if a drop-in alternative driver/polyfill exists, Low if not.symbols is empty but import_file_count > 0 (without blank/dot/wildcard flags) -- diet's coupling analysis may be incomplete. Manually inspect the import files to determine actual API usage before scoring Replaceability. Note the gap in the rationale.Rate each axis High/Med/Low using the data gathered in Phases 1-3. Each axis is anchored to concrete data -- no guesswork.
| Axis | Data Source | High (favors removal) | Medium | Low (discourages removal) |
|---|---|---|---|---|
| Transitive Cleanup | exclusive_transitive | >= 10 exclusive deps | 1-9 exclusive | 0 exclusive |
| Production Scope | Phase 2 classification | 0 production files (all test/CI/example) | < 50% production files | >= 50% production files |
| Coupling Depth | coupling_effort, import_file_count, call_site_count | coupling_effort < 0.25 (trivial/easy) | 0.25 - 0.6 (moderate) | >= 0.6 (hard, deeply wired) |
| Replaceability | Phase 3b symbol map | > 80% symbols have stdlib/existing-dep replacement | 50-80% replaceable | < 50%, or crypto/protocol involved |
| Security Urgency | has_vulnerabilities, max_cvss_score, lifecycle | CVSS >= 7.0, or lifecycle EOL-Confirmed/EOL-Effective/Archived | CVSS 4.0-6.9, or lifecycle Stalled/EOL-Scheduled/Review Needed | No vulns, lifecycle Active/Legacy-Safe |
| Cascade Potential | exclusive_transitive, project knowledge | Removing unblocks 3+ further removals | Unblocks 1-2 | Standalone, no cascade |
coupling_effort. Test/CI refactoring is low-risk regardless of call count.stays_as_indirect: true, exclusive_transitive may overestimate actual cleanup -- some "exclusive" transitives may remain reachable via the indirect path. Note this uncertainty in the rationale.6-Axis Scores:
Transitive Cleanup: {H/M/L} -- {exclusive_transitive} exclusive deps ({reasoning})
Production Scope: {H/M/L} -- {N}/{total} files are production ({reasoning})
Coupling Depth: {H/M/L} -- effort={coupling_effort:.2f}, {call_site_count} calls across {api_breadth} APIs
Replaceability: {H/M/L} -- {N}/{total} symbols replaceable ({reasoning})
Security Urgency: {H/M/L} -- {lifecycle}, {vulnerability_count} vulns, max CVSS {max_cvss_score}
Cascade Potential: {H/M/L} -- {reasoning}
Synthesize all phases into a structured recommendation. Use the exact field names from diet JSON.
### Verdict: {name}@{version}
**Diet Data**:
rank: #{rank}
priority_score: {priority_score:.3f}
difficulty: {difficulty}
ecosystem: {ecosystem}
**Graph**:
exclusive_transitive: {exclusive_transitive}
total_transitive: {total_transitive}
stays_as_indirect: {stays_as_indirect}
indirect_via: {indirect_via or "none"}
**Coupling**:
{import_file_count} files ({N} production, {N} test, {N} other)
{call_site_count} calls across {api_breadth} APIs
symbols: {symbols count} ({N} stdlib-replaceable, {N} self-impl, {N} no-replacement)
**Health**:
lifecycle: {lifecycle}
vulnerabilities: {vulnerability_count} (max CVSS {max_cvss_score})
**API Leakage**: {Yes (BREAKING CHANGE) / No}
**6-Axis Summary**:
{axis}: {H/M/L} (x6, from Phase 4)
**Recommendation**: REMOVE / DEFER / KEEP
**Effort**: Trivial (<1h) / Small (1-4h) / Medium (1-3d) / Large (1w+)
**Priority**: S (do now) / A (this sprint) / B (when convenient) / C (keep for now)
**Rationale**: {1-2 sentences -- why this recommendation, citing the key axis scores}
Rules are evaluated top-to-bottom; first match wins. If multiple rows could match, the earlier row takes priority.
| Condition | Recommendation |
|---|---|
is_unused: true | REMOVE, Trivial, Priority S |
| API leakage = Yes AND Security Urgency = High | REMOVE with BREAKING CHANGE warning -- security overrides API stability. Rationale must document the breaking change and recommend a major version bump or deprecation timeline. Escalate to /diet-assess-risk |
| API leakage = Yes | DEFER (needs major version bump or deprecation period) |
| Replaceability = Low (crypto/protocol) | KEEP -- find an alternative maintained library rather than self-implementing. If Security Urgency is also High, escalate to /diet-assess-risk for full risk analysis. Never recommend self-implementing crypto/protocol |
| All 6 axes High or Med | REMOVE |
| 1-2 axes Low, rest High/Med | REMOVE -- note the Low axes as caveats in rationale |
| Security Urgency = High, others mixed | REMOVE (prioritize security). Suggest /diet-assess-risk for full analysis |
| >= 3 axes Low | KEEP |
stays_as_indirect: true, coupling low | REMOVE (still valuable: delegates version management, unblocks future cleanup) |
| Effort | Criteria |
|---|---|
| Trivial (<1h) | is_unused: true, OR 0 production files, OR all symbols stdlib-replaceable with <= 3 files |
| Small (1-4h) | <= 5 production files AND > 80% symbols replaceable |
| Medium (1-3d) | 6-15 production files, OR 50-80% symbols replaceable, OR API leakage requiring deprecation |
| Large (1w+) | > 15 production files, OR < 50% symbols replaceable, OR crypto/protocol replacement needed |
/diet-assess-risk {name} for full data-flow analysis."/diet-remove {name} --pr for guided implementation."/diet-remove {name} --repo {owner/repo} to file an issue."has_blank_import is true and call_site_count is 0, the import IS the usage. Don't call it "unused."ecosystem field in diet JSON tells you which language. Use the ecosystem-specific commands in Phase 3a.