Without #!/usr/bin/env node on the first line of dist/cli.js, Linux and macOS refuse to execute the file — users see cannot execute binary file: Exec format error and have no way to fix it short of prefixing node manually. Hardcoding #!/usr/local/bin/node is nearly as bad: it breaks on systems using nvm, asdf, volta, Homebrew's Apple Silicon paths, or any container where node lives elsewhere. env-based shebangs are the only portable form.
High because a missing or hardcoded shebang breaks execution on Unix systems for a large fraction of users.
Ensure the entry file's first line is exactly #!/usr/bin/env node (or #!/usr/bin/env python3) with no BOM or blank line before it. If you transpile, configure the bundler to preserve it. With tsup or esbuild in tsup.config.ts:
export default defineConfig({
entry: ['src/cli.ts'],
banner: { js: '#!/usr/bin/env node' },
})
ID: cli-quality.config-distribution.shebang
Severity: high
What to look for: List all executable entry point files. For each, check the CLI entry file (the file referenced by bin in package.json, or the main script for Python). For Node.js, the first line must be #!/usr/bin/env node. For Python, it should be #!/usr/bin/env python3. Check that the shebang is on the very first line (no blank lines or BOM before it). Check that the file has executable permissions (or that the build/publish process sets them).
Pass criteria: The CLI entry file has a correct shebang line as its first line. For compiled languages (Go, Rust), this check is not applicable since the binary is compiled — 100% of executable files must start with a valid shebang (#!/usr/bin/env node or equivalent). Report: "X entry point files found, all Y have correct shebang lines."
Fail criteria: No shebang line, incorrect shebang (hardcoded path like #!/usr/local/bin/node instead of #!/usr/bin/env node), or shebang is not on the first line.
Skip (N/A) when: The CLI is written in a compiled language (Go, Rust) — compiled binaries don't need shebangs. All checks skip when no CLI entry point is detected.
Detail on fail: "Entry file dist/cli.js has no shebang line — running via 'mytool' will fail with 'cannot execute binary file' on Unix systems" or "Shebang uses hardcoded path '#!/usr/local/bin/node' — will fail on systems where node is installed elsewhere"
Remediation: The shebang line tells the OS which interpreter to use:
#!/usr/bin/env node
// ^ This MUST be the very first line of the file
import { program } from 'commander'
// rest of your CLI code
#!/usr/bin/env python3
# ^ First line of your CLI script
import click
If using TypeScript with a build step, ensure your build tool preserves or adds the shebang. With tsup: banner: { js: '#!/usr/bin/env node' }. With esbuild: banner: { js: '#!/usr/bin/env node' }.