Skip to content

fix(cli): detect direct run through npm bin symlinks#39

Merged
stevenobiajulu merged 2 commits intomainfrom
fix/npx-silent-exit
Apr 20, 2026
Merged

fix(cli): detect direct run through npm bin symlinks#39
stevenobiajulu merged 2 commits intomainfrom
fix/npx-silent-exit

Conversation

@stevenobiajulu
Copy link
Copy Markdown
Member

Summary

  • npx email-agent-mcp <anything> was silently exiting 0 with no output — not just setup, even --help. Direct node .../bin/email-agent-mcp.js always worked, so the bug only surfaced through npm bin installs.
  • Root cause: both email-agent-mcp (wrapper) and @usejunior/email-mcp (core) declare a bin named email-agent-mcp. When both are installed, @usejunior/email-mcp wins and ~/.npm-global/bin/email-agent-mcp symlinks to dist/cli.js — not to the wrapper. That's fine except the auto-execute guard at the bottom of cli.js was a brittle suffix check (argv[1].endsWith('cli.js') || argv[1].endsWith('cli.ts')). Via the symlink, argv[1] ends with email-agent-mcp, so the guard was false, runCli never ran, and the module exited cleanly with no work done.
  • Fix: replace the suffix check with a realpathSync(argv[1]) === realpathSync(fileURLToPath(import.meta.url)) comparison, so direct execution through npm bin symlinks is detected the same as direct-path invocation. Extracted the predicate as exported isDirectCliRun for testability.
  • Regression tests added: symlink-based direct run returns true, wrapper-path invocation returns false.
  • Workspace versions bumped to 0.1.6 in lockstep via scripts/bump_version.mjs.

Follow-up (separate PR)

Belt-and-suspenders: drop the bin field from @usejunior/email-mcp/package.json entirely, since the core package isn't meant to be run directly by end users. That would prevent the two packages from fighting over the same bin name at install time and remove this class of bug at the source. Not included here to keep the fix minimal.

Test plan

  • npm run build (all workspaces)
  • npm run test:run -w @usejunior/email-mcp → 137 passed
  • Reproduced the silent-exit via the installed symlinked bin at ~/.npm-global/bin/email-agent-mcp
  • After the fix + rebuild, the same invocation path prints help and progresses into the Microsoft setup banner
  • Post-merge: publish 0.1.6 to npm and verify a fresh npx email-agent-mcp --help from a clean shell prints help

Both email-agent-mcp and @usejunior/email-mcp declare a bin named
email-agent-mcp, and the sibling @usejunior/email-mcp entry wins when
npm installs the wrapper. The installed bin is a symlink to dist/cli.js,
so process.argv[1] ends with "email-agent-mcp" — not "cli.js".

The suffix-based isDirectRun guard returned false in that case, the
module loaded without calling runCli, and the process exited 0 with no
output. That reproduced as the "npx email-agent-mcp --help silently
exits" bug seen downstream.

Replace the suffix check with a realpath-based comparison between
process.argv[1] and the compiled cli.js path. Extract the predicate as
isDirectCliRun so it can be covered by tests for both the symlink and
wrapper cases.
Covers the npm bin symlink direct-run fix in the same release so a
published 0.1.6 restores `npx email-agent-mcp` usability end-to-end.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@stevenobiajulu stevenobiajulu merged commit 8b8e408 into main Apr 20, 2026
14 checks passed
@stevenobiajulu stevenobiajulu deleted the fix/npx-silent-exit branch April 20, 2026 16:09
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