This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine.
What I Built
Stackroots checks a package against the project before anyone installs it. It looks at peers, deprecations, major jumps, and the Node range. If a published release fits, it prints that install command. If none fits, it says do not install.
The ghost is the conflict that is still in the project after you hide it. Antonello Zanini walks through the usual fixes in How To Fix the npm "Conflicting Peer Dependency" Error. One of them is npm install --legacy-peer-deps. That flag tells npm to skip the peer check and install the newest release anyway. The error disappears. The packages are still incompatible.
Nithin D J built guard-install for a different blind spot: supply-chain risk before install. Mohd. Ibrahim Iqbal built Exorcist for the packages already in a package.json: deprecated, duplicated, stale. Stackroots is the moment before the next install. The package is not in the tree yet. The question is whether it belongs there.
Demo
Open stackroots-three.vercel.app. The sample project field-notes is already loaded. It uses React 19.3.0 and Node >=18 <20.
- Type
recomposeand press Check. - Read the result. No published release of recompose accepts this project. Do not install it.
npm install --legacy-peer-depswould only hide this conflict.
The same check from a project folder, on Node 22.18 or newer:
npx @fab-c14/stackroots@0.1.2 check recompose
Exit code 1 means stop. A fix line, when one exists, is the exact npm install name@version. recompose has no fix line on this project, because no published release fits.
Gemma 4 writes a note only after a finding, or when you press Ask Gemma 4. The check does not need a key. The note does. Set GEMINI_API_KEY for that part.
Code
Stackroots
Check a package before it joins a project.
Author: fab-c14
Live site: stackroots-three.vercel.app
Package: @fab-c14/stackroots
Stackroots reads the nearest package.json and lockfile and says whether a package you are about to install fits the roots already there. It prints the finding and, when one exists, the npm install line to use. The check is free.
Give this file to a person or to an agent. The procedure under Instructions for an agent is enough to run the check without the rest of the repository. The same procedure is the project skill at .cursor/skills/stackroots/SKILL.md.
How to run
Node 22.18 or newer.
From the project you want to check:
npx @fab-c14/stackroots report
npx @fab-c14/stackroots check zod
npx @fab-c14/stackroots check zod recompose
npx @fab-c14/stackroots check recompose@0.30.0
npx installs the package from the public npm registry. The command walks up from the current folder until it finds a manifest. It reads package.json…
Repository: github.com/fab-c14/stackroots
Package: npmjs.com/package/@fab-c14/stackroots
How I Built It
The verdict is ordinary code. It reads package.json and the lockfile, then the public npm record for the package. A peer range this project does not satisfy is an error. A missing required peer is a warning. A new major of a package already installed is an error. Node ranges are compared only when engines.node is set. Names passed together count as installed together. A release that drops a required peer is not treated as a fix.
If the release you asked for has errors, Stackroots looks for the newest non-deprecated stable release that has no errors and still declares the same required peers and the same engines.node. That becomes npm install name@version. If nothing qualifies, there is no install command, and the do line says not to install.
Gemma 4 (gemma-4-31b-it) is the open-weight model on the website. It does not run in the command, and it does not run when the page loads. It receives the project name, the Node range, the root names and versions, and the finding. The raw package.json stays in the browser. The call goes to the Gemini API with storage off. I did not run the model on the laptop. If Gemma is down, the do line is still there.
flowchart TD
A[package.json and lockfile] --> B[Stackroots check]
B --> C{Does a published release fit?}
C -->|yes| D["fix line: npm install name@version"]
C -->|no| E[do line: do not install]
B --> F[Gemma 4, only if you ask]
How an agent uses it
The same steps are in the README, under Instructions for an agent. An agent runs them before it adds a dependency.
-
npx @fab-c14/stackroots reportto read the tree. -
npx @fab-c14/stackroots check <package>for each package it means to install. A version looks likename@version. - Exit 0 and no error line: it may install the name it checked.
- A
fixline is the command to run. It does not install the version that was rejected. - A
doline that says not to install means stop. - It does not add
--legacy-peer-deps. It does not invent a version the command did not print. It does not call Gemma from the command.
Why Does Open Innovation Matter?
A closed chat is happy to say "use legacy-peer-deps" and move on. That installs the package and leaves the conflict. Stackroots will not print that flag as a fix.
Gemma 4 can explain a finding. It cannot choose the version. An agent can follow the exit code with no model at all. The package file does not have to leave the machine for the check. Only a short brief of public facts goes out, and only when someone asks for the note.
Exorcist keeps a health scan in the browser so a package.json is not uploaded for scoring. Stackroots keeps the raw file in the browser for a different job: a yes or no on the package you are about to add. Read Exorcist. Read the peer-dependency guide. Read guard-install.
The Spirit of It
I loaded a React 19 project and typed recompose. People still reach for it. Every published release wants an older React. The page did not offer a clever version. It said do not install it, and it said the legacy flag would only hide the conflict.
That is the ghost. It is a library that still installs if you tell npm to look away.
Prize Categories
-
Best Use of Gemma. Gemma 4 (
gemma-4-31b-it) writes the note and answers the chat on the website, only after a finding or a button press. The check does not call it.
Top comments (0)