Skip to content

fix(cli): add Node-compatible bin wrapper for npm publish#1636

Merged
braden-w merged 1 commit intomainfrom
fix/cli-bin-wrapper
Apr 7, 2026
Merged

fix(cli): add Node-compatible bin wrapper for npm publish#1636
braden-w merged 1 commit intomainfrom
fix/cli-bin-wrapper

Conversation

@braden-w
Copy link
Copy Markdown
Member

@braden-w braden-w commented Apr 7, 2026

Problem

npm publish strips .ts files from the bin field. Our CLI's bin pointed to ./src/bin.ts, so after publish, npx epicenter and bunx epicenter would fail — npm silently removes the bin entry from the published package.

Solution

Added bin/epicenter.mjs — a Node-compatible wrapper that handles three runtime scenarios:

Running under Bun (the happy path): The wrapper detects typeof Bun !== "undefined" and directly imports ../src/bin.ts. Zero overhead.

Running under Node, Bun installed: The wrapper detects it's in Node, checks if Bun is on PATH via spawnSync("bun", ["--version"]), and re-execs itself with Bun. This handles npx epicenter on machines that have both runtimes.

Running under Node, no Bun: Prints a clear error message with install instructions and exits.

Why #!/usr/bin/env node instead of #!/usr/bin/env bun

npx on Windows ignores shebangs and always invokes with Node. A #!/usr/bin/env bun shebang would produce a cryptic OS-level error (bun: No such file or directory) on machines without Bun. Using node as the entry point guarantees the script always launches, then makes an intelligent decision about how to proceed.

Why not compile to JS

The CLI imports .ts files and uses Bun-specific APIs (bun:sqlite). Compiling to JavaScript wouldn't help — the output still can't run on Node. Bun is a genuine runtime requirement, not a build preference.

Prior art

This is the established pattern for Bun-first CLIs on npm:

  • trekker — same #!/usr/bin/env node + Bun detect + re-exec
  • pensar — same pattern with ENOENT handling
  • t1code — same pattern

Tested

Scenario Result
bun epicenter.mjs --help ✅ Direct import, no subprocess
node epicenter.mjs --help ✅ Re-execs with Bun
Node without Bun on PATH ✅ Prints install instructions, exits 1

Changelog

  • Fix CLI binary not working after npm publish

npm strips .ts files from bin entries. Added a .mjs wrapper that:
- Runs directly when invoked by Bun (imports src/bin.ts)
- Re-execs itself with Bun when invoked by Node
- Prints install instructions if Bun is not available

Pattern follows trekker, t1code, and other Bun-first CLIs.
@braden-w braden-w merged commit 2b78330 into main Apr 7, 2026
1 of 9 checks passed
@braden-w braden-w deleted the fix/cli-bin-wrapper branch April 7, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant