An assistant hands you twenty-two import lines. Most of those packages are real. Some of them
are not — models invent plausible names, and they invent the same plausible names
repeatedly, which is what makes the invented ones worth registering. Somebody registers
langchain-communty and waits for the next person to paste it into a terminal.
You cannot tell the difference by reading. Both names look like packages, because a model that is good at writing code is by construction good at inventing names that look like packages.
pip install and npm install commands, a
requirements.txt or a package.json — all read the same way. The
text stays in this tab.
your-api-key. Each one is listed with its reason, because a
tool that quietly ignores half its input looks exactly like one that found nothing wrong.
import cv2 installs opencv-python; import yaml
installs PyYAML. Every substitution is shown next to the row, and where the
answer is genuinely ambiguous — psycopg2 versus
psycopg2-binary — you are offered both rather than given one.
requirements.txt carrying PyPI's own sha256 for every file, a pinned
package.json, an unresolved.txt of every name that does not exist
with its closest real neighbours, and a CSV of the whole evidence table.
ms, qs and ws are three real npm packages, each one edit
from the other two, each downloaded tens of millions of times a week. Any rule of the form
“warn when a name is one edit from a popular package” fires on all three, and a tool that cries
wolf in its first ten seconds does not get a second look.
So the edit budget scales with the length of the name — one edit in
ws is half the name, one edit in python-dateutil is nothing — and a
neighbour is only mentioned when it is far more popular than the package
being checked, because a squat imitates something bigger than itself. Both are yours to set,
with the live match list underneath so you are calibrating against real matches instead of
trusting a default.
True Damerau-Levenshtein — insert, delete, substitute and transpose — with a substitution
between two keys that are physically adjacent on the keyboard charged at half price, because
reqeusts is a likelier slip than rezuests. The names are indexed in a
BK-tree so the whole table is re-searched between two frames of a slider drag.
localStorage; package names never do. A
build gate checks the shipped JavaScript for the storage APIs it must not contain.
GETs of public URLs, sent
with credentials omitted, and they necessarily reveal your IP address and which names you
asked about. Watch them go past in the event log — it lists every request by name, and the
list should match your browser's own network panel exactly.
registry.npmjs.org and api.npmjs.org for npm metadata and download counts.pypi.org for the PyPI simple index — existence, versions, upload dates and sha256 digests.feedback.benrichardson.dev. Nothing is
sent unless you open the feedback form and press Send; your files and data never are.
pypistats.org, which
sends no access-control-allow-origin header at all, so a browser cannot read
them. The column says n/a rather than borrowing a number from somewhere else.
pip --require-hashes needs every transitive dependency pinned and
hashed too, and ghostdep only ever sees the names you pasted.
ghostdep checks whether the packages in an AI-generated answer actually exist, how old they are, and whether any of them is a keystroke away from something far more popular — then hands you a pinned, hash-locked dependency file.
It is built and maintained by Ben Richardson. The rest of the catalogue of tools and sites lives at lab.benrichardson.dev.
Source: github.com/ben-gy/ghostdep.
The sample answer is fictional and was written for this tool. It contains real package names and invented ones on purpose; no real package in it is being accused of anything.