CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

npx-package

Provides patterns for building TypeScript CLI tools distributed via npx - package structure, the bin entry point and shebang, ESM-ready tsconfig, argument parsing, output and exit codes, local testing, and npm publishing. Use when the user asks to "create an npx package", "build a CLI tool with TypeScript", "set up a Node.js CLI", "publish an npm package", "configure package.json bin field", "add CLI argument parsing", or "create an executable npm package", or when writing TypeScript code for a command-line tool distributed via npx. Does not cover general TypeScript style, type design, or non-CLI project layout - see the typescript skill for those.

74

Quality

93%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

npx Package Development (TypeScript)

Patterns for TypeScript CLI tools that run via npx: a standard npm package with a bin entry point.

Package Structure

my-cli/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts          # entry point (bin target)
│   ├── cli.ts            # argument parsing and command routing
│   └── commands/         # command implementations
│       └── install.ts
├── dist/                 # compiled output (gitignored)
└── README.md

package.json Essentials

{
  "name": "my-cli",
  "version": "1.0.0",
  "description": "CLI tool description",
  "type": "module",
  "bin": {
    "my-cli": "./dist/index.js"
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "prepublishOnly": "npm run build"
  },
  "engines": {
    "node": ">=24"
  },
  "devDependencies": {
    "typescript": "^7",
    "@types/node": "^24"
  }
}

Set engines.node to the current Node LTS major and keep the @types/node major equal to it. The devDependency ranges above are examples — install with npm install -D typescript @types/node to get the latest majors rather than copying these pins.

Key fields:

FieldPurpose
namePackage name on npm. Use @scope/name for scoped packages
binMaps command names to compiled JS entry points
filesWhitelist of files included in the published package
type: "module"Enable ESM (import/export syntax)
enginesMinimum Node.js version requirement
prepublishOnlyAuto-builds before npm publish

Entry Point with Shebang

The entry point file must start with a Node.js shebang:

#!/usr/bin/env node

// src/index.ts
import { run } from "./cli.js";

run(process.argv.slice(2));

Important: The shebang must be the very first line — no blank lines or comments above it — or npx execution fails on Unix.

TypeScript Configuration

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Key settings:

  • module: "Node16" + moduleResolution: "Node16" — required for proper ESM support in Node.js.
  • outDir: "./dist" — compiled JS goes to dist/, matching the bin path in package.json.
  • declaration: true — emit .d.ts files for consumers who import the package programmatically.

ESM import rule: When using type: "module", all relative imports must include the .js extension, even in .ts source files:

// Correct
import { run } from "./cli.js";

// Wrong — will fail at runtime
import { run } from "./cli";

Argument Parsing

Manual parsing (zero dependencies)

For simple CLIs with 1-3 commands:

// src/cli.ts
export function run(args: string[]): void {
  const command = args[0];

  switch (command) {
    case "install":
      handleInstall(args.slice(1));
      break;
    case "search":
      handleSearch(args.slice(1));
      break;
    case "--help":
    case "-h":
      printHelp();
      break;
    case "--version":
    case "-v":
      printVersion();
      break;
    default:
      console.error(`Unknown command: ${command}`);
      printHelp();
      process.exit(1);
  }
}

Flag extraction helper

function getFlag(args: string[], flag: string): string | undefined {
  const index = args.indexOf(flag);
  if (index === -1 || index + 1 >= args.length) return undefined;
  return args[index + 1];
}

// Usage: npx my-cli install --name my-plugin
const name = getFlag(args, "--name");

With a parsing library (for complex CLIs)

For CLIs with many commands, flags, and subcommands, use commander — see references/patterns.md.

Output and Exit Codes

Colored output (zero dependencies)

Node.js supports ANSI escape codes natively:

const red = (s: string) => `\x1b[31m${s}\x1b[0m`;
const green = (s: string) => `\x1b[32m${s}\x1b[0m`;
const bold = (s: string) => `\x1b[1m${s}\x1b[0m`;
const dim = (s: string) => `\x1b[2m${s}\x1b[0m`;

console.log(green("✓ Installed successfully"));
console.error(red("✗ Installation failed"));

Exit codes

process.exit(0);  // success
process.exit(1);  // general error
process.exit(2);  // misuse (bad arguments)

Always call process.exit() with the appropriate code on failure.

Stderr vs stdout

Program output (data, results) goes to stdout via console.log(); diagnostics (errors, warnings, progress) go to stderr via console.error().

Build and Test Locally

# Build
npm run build

# Test locally (symlinks the package globally)
npm link
my-cli --help

# Or test with npx directly
npx . install --name my-plugin

# Unlink when done
npm unlink -g my-cli

Publishing to npm

# Login (one-time)
npm login

# Publish (runs prepublishOnly → build automatically)
npm publish

# Publish scoped package as public
npm publish --access public

After publishing, the package is immediately available via:

npx my-cli install --name my-plugin

Versioning

npm version patch   # 1.0.0 → 1.0.1
npm version minor   # 1.0.0 → 1.1.0
npm version major   # 1.0.0 → 2.0.0

Run npm version before npm publish — it updates package.json and creates a git tag.

Common Mistakes

MistakeFix
Missing shebangAdd #!/usr/bin/env node as first line of entry point
Shebang not on first lineRemove blank lines/comments above #!/usr/bin/env node
Missing .js in ESM importsAdd .js extension to all relative imports
bin points to .ts filePoint to compiled .js in dist/
dist/ not in filesAdd "files": ["dist"] to package.json
No prepublishOnly scriptAdd "prepublishOnly": "npm run build"
File not executable on UnixRun chmod +x dist/index.js (usually handled by npm)

Additional Resources

Reference Files

For detailed patterns and advanced configuration, consult:

  • references/patterns.md — Commander argument parsing, interactive prompts, spinner/progress indicator, HTTP requests from CLI, error handling patterns, file system and subprocess helpers, environment variables, testing CLI commands
  • references/package-setup.md — Advanced package.json fields, scoped packages, bundling with tsup, dual CJS/ESM support, CI/CD publishing, npm provenance, version management, verifying and post-publish testing
Repository

provectus/awos-recruitment

Path

.claude/skills/npx-package/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.