ERR_MODULE_NOT_FOUND, in a file that was right there
2026-09-01
Scaffolded a fresh project with npx create-loopengine@latest, ran npm install, ran npm run dev — and it crashed immediately: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../node_modules/loopengine/run-agent.js' imported from '.../node_modules/loopengine/dist/index.js'. Nothing had touched the model yet, nothing had touched the agent registry — the crash happened importing loopengine itself.
The cause was package.json's own imports field: "#*": "./*". Node resolves a #-prefixed subpath import relative to the nearest package.json, not to wherever the importing file happens to sit. Inside the repo, that's invisible — core/ lives right next to that package.json, so #core/run-agent.js resolves correctly during development without anyone thinking about it. But a published install only ever ships dist/, and dist/index.js's own import of #run-agent.js still resolved against the package root, not dist/ — looking for a file that was never published, one directory away from the one that was.
It wasn't just that one import, either. Tracing the real dependency graph of both published entry points (index.js and bin/cli.js) turned up the same gap for half a dozen further files run-agent.ts actually imports — budget.ts, compaction.ts, recovery.ts, the entire httpNotifier channel system — none of them in the package's own files allowlist, which had quietly drifted out of date behind real feature work landing in core/.
The fix: give dist/ its own package.json at build time, carrying the identical "#*": "./*" mapping — Node walks up to the nearest package.json it can find, so the same specifier now resolves relative to dist/ for anyone who installed the package, without changing what it means for local development at all. Verified by npm pack, installing the tarball into a fresh scaffold, and watching the same request that used to crash on import instead reach all the way to the model call.