package-choice
Runs a dependency decision tree and recommends stdlib, an existing dep, a default pick or a small local function.
looks for
- a new entry in package.json / requirements / Cargo.toml
- two libraries that overlap (two date libs, two HTTP clients)
- a heavy package used for one function
- an unmaintained, unlicensed or copyleft package
outputs
- → a recommendation (no dep / existing dep / named package + version)
- → a short scorecard (size, license, last release, downloads, deps, scripts)
SKILL.md
Package choice
Every dependency is code you ship, audit and upgrade forever. Add one only when it clearly beats writing and owning the code yourself.
Decision tree
- Is the need real now? Not "might need later". No → stop.
- Does the platform already do it? (
fetch,URL,Intl,structuredClone,crypto.randomUUID,AbortController,Array.prototype.toSorted, CSS:has/container queries,<dialog>.) Yes → use it. - Does an existing dependency already do it? Yes → use that. Never two libraries for one job.
- Is it under ~30 lines you understand and can test? Yes → write it. (Debounce, clamp, a slugify, a tiny event emitter.)
- Otherwise pick a package and score it:
| Check | Pass | Red flag |
|---|---|---|
| License | MIT, Apache-2.0, BSD, ISC | none, unclear, AGPL/GPL in a closed product, custom |
| Maintenance | release or commit within ~12 months, issues answered | archived, single maintainer gone quiet |
| Adoption | widely used, used by projects you trust | brand new with no users |
| Size | small, tree-shakeable, ESM | pulls in a large transitive tree |
| Install scripts | none | postinstall that downloads or runs code |
| Types | built-in TS types | none and no @types |
| Security | no open critical advisories | repeated advisories, typo-squat-looking name |
| Fit | does one job well | a framework for a one-function need |
- Pin and record. Exact version in the lockfile; one line in the PR or an ADR saying why this package and what was rejected.
Need → default pick (web / TypeScript)
See references/defaults.md for the full table. Short version:
| Need | Default |
|---|---|
| Schema validation | zod |
| Dates | native Intl / Temporal; date-fns if you need more |
| Server state / fetching (React) | TanStack Query |
| Client state (small) | zustand |
| Forms (React) | react-hook-form + zod |
| Icons | lucide-react |
| Unit tests | vitest |
| E2E tests | Playwright |
| Animation (React) | motion |
| Markdown | react-markdown + remark-gfm |
| Zip in browser | fflate |
| IDs | crypto.randomUUID() |
| Class names | clsx (+ tailwind-merge with Tailwind) |
Removing is a win
When you touch an area, check for dependencies you can drop (replaced by the platform, used once, duplicated). Note the bundle-size saving.
Customize for your project
{{LANGUAGE}}/{{PACKAGE_MANAGER}}and your own default-pick table.- Licenses your company forbids (
{{FORBIDDEN_LICENSES}}). - The audit command you run in CI (e.g.
npm audit --omit=dev).
pairs with
- personatrueorfalse
tech-lead
Locks contracts and architecture decisions before specialists build, and unblocks them when the design is unclear.
- a change that crosses module or service boundaries
- a new or changed endpoint, event or table
- a proposed new dependency
Claude · Codex · ChatGPT - personatrueorfalse
engineer-mindset
Applies a clarify → smallest change → verify → explain loop so changes are small, correct and honest.
- a vague or underspecified request
- urge to refactor, generalize or add a dependency
- a fix that touches many files
Claude · Codex · ChatGPT - skilltrueorfalse
adr
Captures one decision with its context, rejected options and consequences in a numbered, immutable record.
- a technology or vendor choice
- a change that is hard to reverse
- a decision made in chat that nobody wrote down
Claude · Codex · ChatGPT - ruletrueorfalse
agent-directives
Gives every agent the same baseline rules on scope, reuse, reading order and verification.
- a new agent or session starting in the repo
- blind repo-wide scans
- new dependencies added without a check
Claude · Codex · ChatGPT