Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
jwynia avatar

Npx Cli

  • 283 installs
  • 133 repo stars
  • Updated February 24, 2026
  • jwynia/agent-skills

npx-cli is a CLI development agent skill that scaffolds, tests, bundles, and publishes npx-executable command-line tools using Bun, citty, Bunup, and Vitest for developers shipping npm CLI packages.

About

npx-cli is a CLI toolchain skill in jwynia/agent-skills—part of a 112-skill collection—that teaches building and publishing npx-executable command-line tools with Bun as the primary runtime while producing Node.js-compatible npm binaries. The SKILL.md covers project scaffolding, citty argument parsing with runMain() and sub-commands, picocolors terminal UX, strict TypeScript with module nodenext, Biome v2 formatting, ESLint with typescript-eslint, Vitest testing, Bunup dual-entry bundling for library and CLI outputs, Changesets versioning, and npm publish --provenance releases. Key rules enforce files whitelist packaging, Node shebangs on published bins, and separating thin CLI wiring from importable core modules. Developers reach for npx-cli when creating a new agent-invokable CLI, adding a bin entry to an existing library, or publishing typed tools consumable via npx without manual shell choreography. Install with npx skills add jwynia/agent-skills --skill npx-cli from the skills/tech/development/tooling path.

  • npx package execution
  • CLI scaffolding
  • Third-party tool wiring
  • Non-interactive runs
  • Agent-safe shell patterns

Npx Cli by the numbers

  • 283 all-time installs (skills.sh)
  • +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
  • Ranked #177 of 550 CLI & Terminal skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jwynia/agent-skills --skill npx-cli

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs283
repo stars133
Last updatedFebruary 24, 2026
Repositoryjwynia/agent-skills

How do you build and publish an npx CLI?

Run and scaffold npm/npx CLI packages from agent sessions to bootstrap tools, codegen utilities, and third-party CLIs without manual shell choreography.

Who is it for?

Developers creating npx-installable CLI tools who want a Bun-first scaffold with citty, Bunup, Vitest, and provenance publishing patterns.

Skip if: Teams running third-party CLIs only once via npx without authoring a package, or Python/Rust CLI projects outside the Bun/npm ecosystem.

When should I use this skill?

The user wants to create a new CLI tool, add an npx bin to a package, configure citty sub-commands, or publish a command-line utility to npm.

What you get

Scaffolded CLI package, citty command parser, Bunup bundles with .d.ts types, Vitest test suite, and npm-publishable bin entry.

  • CLI package scaffold
  • Bundled bin entry
  • Vitest test suite

By the numbers

  • Part of jwynia/agent-skills collection with 112 reusable agent skills
  • Documents toolchain stack: Bun, citty, Bunup, Vitest, Biome v2, and Changesets

Files

SKILL.mdMarkdownGitHub ↗

npx CLI Tool Development (Bun-First)

Build and publish npx-executable command-line tools using Bun as the primary runtime and toolchain, producing binaries that work for all npm/npx users (Node.js runtime).

When to Use This Skill

Use when:

  • Creating a new CLI tool from scratch
  • Building an npx-executable binary
  • Setting up argument parsing, sub-commands, or terminal UX for a CLI
  • Publishing a CLI tool to npm
  • Adding a CLI to an existing library package

Do NOT use when:

  • Building a library without a CLI (use the npm-package skill)
  • Building an application (not a published package)
  • Working in a monorepo (this skill targets single-package repos)

Toolchain

ConcernToolWhy
Runtime / package managerBunFast install, run, transpile
BundlerBunupBun-native, dual entry (lib + cli), .d.ts
Argument parsingcitty~3KB, TypeScript-native, auto-help, runMain()
Terminal colorspicocolors~7KB, CJS+ESM, auto-detect
TypeScriptmodule: "nodenext", strict: true + extrasMaximum correctness
Formatting + basic lintingBiome v2Fast, single tool
Type-aware lintingESLint + typescript-eslintDeep type safety
TestingVitestIsolation, mocking, coverage
VersioningChangesetsFile-based, explicit
Publishingnpm publish --provenanceTrusted Publishing / OIDC

Scaffolding a New CLI

Run the scaffold script:

bun run <skill-path>/scripts/scaffold.ts ./my-cli \
  --name my-cli \
  --bin my-cli \
  --description "What this CLI does" \
  --author "Your Name" \
  --license MIT

Options:

  • --bin <name> — Binary name for npx (defaults to package name without scope)
  • --cli-only — No library exports, CLI binary only
  • --no-eslint — Skip ESLint, use Biome only

Then install dependencies:

cd my-cli
bun install
bun add -d bunup typescript vitest @vitest/coverage-v8 @biomejs/biome @changesets/cli
bun add citty picocolors
bun add -d eslint typescript-eslint  # unless --no-eslint

Project Structure

Dual (Library + CLI) — Default

my-cli/
├── src/
│   ├── index.ts            # Library exports (programmatic API)
│   ├── index.test.ts       # Unit tests for library
│   ├── cli.ts              # CLI entry point (imports from index.ts)
│   └── cli.test.ts         # CLI integration tests
├── dist/
│   ├── index.js            # Library bundle
│   ├── index.d.ts          # Type declarations
│   └── cli.js              # CLI binary (with shebang)
├── .changeset/
│   └── config.json
├── package.json
├── tsconfig.json
├── bunup.config.ts
├── biome.json
├── eslint.config.ts
├── vitest.config.ts
├── .gitignore
├── README.md
└── LICENSE

CLI-Only (No Library Exports)

Same structure minus src/index.ts and src/index.test.ts. No exports field in package.json, only bin.

Architecture Pattern

Separate logic from CLI wiring. The CLI entry (cli.ts) is a thin wrapper that: 1. Parses arguments with citty 2. Calls into the library/core modules 3. Formats output for the terminal

All business logic lives in importable modules (index.ts or internal modules). This makes logic unit-testable without spawning processes.

cli.ts → imports from → index.ts / core modules
                              ↑
                         unit tests

Key Rules (Non-Negotiable)

All rules from the npm-package skill apply here. These additional rules are specific to CLI packages:

Binary Configuration

1. Always use `#!/usr/bin/env node` in published bin files. Never #!/usr/bin/env bun. The vast majority of npx users don't have Bun installed.

2. Point `bin` at compiled JavaScript in `dist/`. Never at TypeScript source. npx consumers won't have your build toolchain.

3. Ensure the bin file is executable. The build script includes chmod +x dist/cli.js after compilation.

4. Build with Node.js as the target. Bunup's output must run on Node.js, not require Bun runtime features.

Package Configuration

5. Always use `"type": "module"` in package.json.

6. `types` must be the first condition in every exports block.

7. Use `files: ["dist"]`. Whitelist only.

8. For dual packages (library + CLI): The exports field exposes the library API. The bin field exposes the CLI. They are independent — bin is NOT part of exports.

Code Quality

9. `any` is banned. Use unknown and narrow.

10. Use `import type` for type-only imports.

11. Handle errors gracefully. CLI users should never see raw stack traces. Use citty's runMain() which handles this automatically, plus process.on('SIGINT', ...) for cleanup.

12. Exit with appropriate codes. 0 for success, 1 for errors, 2 for bad arguments, 130 for SIGINT.

Reference Documentation

Read these before modifying configuration:

  • [reference/cli-patterns.md](./reference/cli-patterns.md) — bin setup, citty patterns, sub-commands, error handling, terminal UX, testing CLI binaries
  • [reference/esm-cjs-guide.md](./reference/esm-cjs-guide.md)exports map, dual package hazard, common mistakes
  • [reference/strict-typescript.md](./reference/strict-typescript.md) — tsconfig, Biome rules, ESLint type-aware rules, Vitest config
  • [reference/publishing-workflow.md](./reference/publishing-workflow.md) — Changesets, files field, Trusted Publishing, CI pipeline

Argument Parsing with citty

Single Command

import { defineCommand, runMain } from 'citty';

const main = defineCommand({
  meta: { name: 'my-cli', version: '1.0.0', description: '...' },
  args: {
    input: { type: 'positional', description: 'Input file', required: true },
    output: { alias: 'o', type: 'string', description: 'Output path', default: './out' },
    verbose: { alias: 'v', type: 'boolean', description: 'Verbose output', default: false },
  },
  run({ args }) {
    // args is fully typed
  },
});

void runMain(main);

Sub-Commands

import { defineCommand, runMain } from 'citty';

const init = defineCommand({ meta: { name: 'init' }, /* ... */ });
const build = defineCommand({ meta: { name: 'build' }, /* ... */ });

const main = defineCommand({
  meta: { name: 'my-cli', version: '1.0.0' },
  subCommands: { init, build },
});

void runMain(main);

See reference/cli-patterns.md for complete examples including error handling, colors, and spinners.

Testing Strategy

Unit Tests — Test the Logic

// src/index.test.ts
import { describe, it, expect } from 'vitest';
import { processInput } from './index.js';

describe('processInput', () => {
  it('handles valid input', () => {
    expect(processInput('test')).toBe('expected');
  });
});

Integration Tests — Test the Binary

Build first (bun run build), then spawn the compiled binary:

// src/cli.test.ts
import { describe, it, expect } from 'vitest';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const exec = promisify(execFile);

describe('CLI', () => {
  it('prints help', async () => {
    const { stdout } = await exec('node', ['./dist/cli.js', '--help']);
    expect(stdout).toContain('my-cli');
  });
});

Development Workflow

# Write code and tests
bun run test:watch    # Vitest watch mode

# Check everything
bun run lint          # Biome + ESLint
bun run typecheck     # tsc --noEmit
bun run test          # Vitest

# Build and try the CLI locally
bun run build
node ./dist/cli.js --help
node ./dist/cli.js some-input

# Prepare release
bunx changeset
bunx changeset version

# Publish
bun run release       # Build + npm publish --provenance

Adding Sub-Commands Later

1. Create a new file per sub-command: src/commands/init.ts, src/commands/build.ts 2. Each exports a defineCommand() result 3. Import and wire into the main command's subCommands 4. Keep logic in testable modules, commands are thin wrappers

Converting a CLI-Only Package to Dual (Library + CLI)

1. Create src/index.ts with the public API 2. Update bunup.config.ts to include both entry points 3. Add exports field to package.json alongside the existing bin 4. Add .d.ts generation: dts: { entry: ['src/index.ts'] }

Bun-Specific Gotchas

  • `bun build` does not generate .d.ts files. Use Bunup or tsc --emitDeclarationOnly.
  • `bun build` does not downlevel syntax. ES2022+ ships as-is.
  • `bun publish` does not support `--provenance`. Use npm publish.
  • `bun publish` uses `NPM_CONFIG_TOKEN`, not NODE_AUTH_TOKEN.
  • Never use `#!/usr/bin/env bun` in published packages. Your users don't have Bun.
  • Bunup `banner` adds the shebang to ALL output files, including the library entry. If this is a problem, use a post-build script to add the shebang only to dist/cli.js.

Related skills

How it compares

Pick npx-cli for authoring publishable npm CLIs with Bun; use generic shell skills only for one-off npx invocations of existing third-party tools.

FAQ

What toolchain does npx-cli recommend for CLI development?

npx-cli recommends Bun for install and run, citty for argument parsing, Bunup for dual lib and CLI bundling with .d.ts, Vitest for tests, Biome v2 plus ESLint for linting, and Changesets with npm publish --provenance for releases.

Does npx-cli produce Node-compatible binaries?

npx-cli uses Bun as the primary toolchain but bundles CLI output with a Node shebang so published packages run for all npm and npx users on the Node.js runtime.

When should agents load the npx-cli skill?

npx-cli loads when a user wants to create a new CLI from scratch, add an npx bin to a library, configure sub-commands and terminal UX, or publish a typed command-line package to npm.

CLI & Terminalintegrationsdevops

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.