Dual-purpose: upfront selection when adding a new dep, and review when a PR adds or bumps one. Same evaluation criteria either way, with per-language addenda for ecosystem-specific quirks.
The canonical package coordinate usually has ecosystem-specific gotchas that override the general criteria. The wrong coordinate is the wrong dep, no matter how good the package is.
Future addenda: npm (scoped vs unscoped, deprecated packages, ESM/CJS), Python (package vs distribution name, wheel/sdist, ABI compatibility), Rust (pre-1.0 churn, crate renames), Ruby (transitive native-extension hazards). Add when a real evaluation surfaces the need.
Run through these. Cite specific data: no hand-waving, no I think.
-
License. Detect via the ecosystem's registry or the repo's LICENSE / LICENSE.md / COPYING. Permissive (MIT, BSD, Apache-2.0, ISC, MPL-2.0) is generally fine. Copyleft (GPL, AGPL, LGPL) needs an explicit compatibility check against the user's project license. No license = no use. Note relicense history; relicenses are a red flag.
-
Internal precedent. Before weighing external popularity, check whether the candidate (or an equivalent) is already used or wrapped internally. Sources, in order:
- Project-local hints:
CLAUDE.md, REVIEW.md, .claude/rules/, and the manifests of sibling services if mounted locally.
- Internal code search if available: Sourcegraph (
mcp__sourcegraph__keyword_search / mcp__sourcegraph__list_repos), gh search code scoped to the user's orgs.
- Internal package registry / module index if the project documents one.
A candidate with strong internal precedent beats a marginally more-popular external alternative — consistency, shared CVE response, shared tooling all compound. An internal wrapper or replacement (e.g. an internal/x package covering the same need) usually means don't add the external dep; use the internal one. If no internal-search tooling is reachable, mark this row unsure: no internal index available rather than skipping.
-
Popularity / community signal. Stars (rough proxy), the ecosystem's "depended by" / "imported by" count, Sourcegraph importer search if available. Popularity without recent maintenance is a trap, not validation.
-
Maintenance / activity. Last commit date: <12 months = active, 12–24 = stale, >24 = abandoned. Last release vs last commit (releases lagging commits = pre-release work or maintainer absent). Open issues / PRs ratio; unanswered bug reports older than 6 months; "looking for maintainers" signals.
-
Vulnerability history. Check the ecosystem's vuln DB (vuln.go.dev for Go, GitHub Advisory DB, OSV, npm audit, pip-audit, RustSec). Pattern of repeat CVEs in the same subsystem = avoid.
-
API stability. Semver discipline. Major-version bump cadence: every six months is chaotic. v0.x.y after years signals unstable surface. Look for a documented breaking-changes / release-notes practice.
-
Surface and transitive cost. Each dep brings its own deps. Count transitive deps; smaller is better when alternatives are comparable. Note pulled-in transitive risk (e.g. a Go module that quietly pulls in a deprecated logger).
-
Fit + stdlib-first. Does it do what's needed with minimal surface? A library doing 10× what's needed brings 10× the risk. Before weighing fit, check whether the standard library or built-ins already cover the need — an explicit gate, not an afterthought. If they do, the candidate is CAUTION (needless dependency) or NO-GO. Many ecosystems' stdlib covers what third-party libs once owned (Go's slices, maps, cmp, errors, log/slog; Python's pathlib, dataclasses; JS's URL, fetch, structuredClone). Go additionally has a quasi-standard tier, golang.org/x/* (Go-team-maintained: x/sync, x/time, x/text, x/crypto, …), sitting between stdlib and third-party — prefer it. Ordering: stdlib > golang.org/x > third-party. See references/go.md → Standard library & golang.org/x first for the needs→package table.
-
Footguns. Ecosystem-specific (init-time side effects, monkey-patching, peer-dep churn, global mutable state, unbounded goroutines/threads). Check the language reference for known patterns; surface anything weird in the README or top-level types.
-
Capabilities. What privileged operations can the dep (and its transitive closure) reach? A logging library that transitively pulls os/exec or net/http is doing more than it advertises. For Go, capslock (see references/go.md) classifies transitive calls into capability buckets (NETWORK, FILES, EXEC, REFLECT, CGO, UNSAFE_POINTER, ARBITRARY_EXECUTION, etc.). Two modes:
-
Confirm scope (per confirm-before-implementing): selection (prospective add) or review (PR touched manifest). Identify the language and the candidate package.
-
Resolve canonical coordinate. Read the per-language reference if one exists; apply its rules first. This is non-negotiable. Examples: Go's /vN path probe, npm's scope check, Python's distribution-name lookup.
-
Survey alternatives (selection mode, optional). When multiple candidates are in the running, delegate per-candidate data lookups to Explore subagents (model: haiku per subagent-model-routing — mechanical lookup across 11 fixed criteria; per parallelize-subagents and delegate-investigation). Each subagent returns one verdict table prefixed with Status: per subagent-prompt-contract. Parent merges and applies the final verdict synthesis (model: opus per subagent-model-routing — hard reasoning combining multiple criteria across candidates).
-
Run the 11 criteria against the resolved coordinate. Cite specific numbers, dates, license names.
-
Produce verdict: GO / CAUTION / NO-GO. Always include the coordinate to use explicitly (so the user doesn't import the wrong major / wrong scope / wrong distribution).
-
Review mode: output as a finding row suitable for review-code or review-security finding tables, wrapped per the existing path:line deep-link convention if pr_url is set. Severity prefix per terse-comments: risk: (CAUTION) or bug: (NO-GO).