npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jshow/cli

v1.0.11

Published

A simple CLI tool for j-show

Readme


Overview

@jshow/cli is a command line tool set for jShow, developed based on commander. It provides a powerful and extensible CLI framework with automatic command discovery, plugin system, and full TypeScript support.

The CLI automatically scans and loads command files (.cmd.ts or .cmd.js) and plugin files (.plugin.ts or .plugin.js) from your project, making it easy to build custom CLI tools with minimal configuration.


Features

| Feature | Description | | --- | --- | | Auto Command Discovery | Automatically scans and loads command files (.cmd.ts or .cmd.js) in the project | | Plugin System | Support for plugins with lifecycle hooks (beforeExecute, afterExecute) | | Command Registration | Support custom command registration for easy command extension | | Type Safety | Full TypeScript type support | | Commander Export | Exports the entire commander library for direct use | | Command Base Class | Provides BaseCommand abstract base class to simplify command development | | Command Grouping | Organize commands into groups for better help information display | | Workspace utilities | Re-exported helpers (getGroupPackages, Git wrappers, execSync, etc.) for release/backup flows and your own tooling |


Requirements

  • Node.js: 18+ recommended (library targets modern Node with ESM).
  • pnpm: >=10 when working in this repository (package.json engines).
  • TypeScript: Optional at runtime; bin/cli.mjs loads ts-node/esm so .cmd.ts / .plugin.ts in the consumer project can be discovered.

Why @jshow/cli?

  • Zero Configuration – Automatically discovers and loads commands/plugins from your project without manual registration.
  • Type-Safe – Full TypeScript support with comprehensive type definitions for commands, plugins, and options.
  • Extensible – Plugin system allows you to add cross-cutting concerns like logging, timing, and error handling.
  • Commander Integration – Built on top of Commander.js, exports the entire library for advanced use cases.
  • Developer Friendly – Simple base classes and clear conventions make it easy to create and maintain commands.
  • Modern Tooling – Built for modern Node.js with ESM support and TypeScript-first design.

Quick Start

  1. Install dependencies

    pnpm add @jshow/cli
    # or
    npm install @jshow/cli
    # or
    yarn add @jshow/cli
  2. Create a command file

    Create a file ending with .cmd.ts or .cmd.js in your project:

    // example.cmd.ts
    import { BaseCommand, type CommandContext } from '@jshow/cli';
    
    export default class ExampleCommand extends BaseCommand {
      static name = 'example';
      static force = false;
    
      public get args() {
        return {
          name: 'example',
          description: 'This is an example command',
          aliases: ['ex', 'e'],
          group: 'examples',
          plugins: ['logger', 'timer'], // Optional: specify plugins to use
          options: [
            {
              name: 'name',
              abbr: 'n',
              flagValue: true,
              description: 'Name parameter',
              defaultValue: 'world',
              required: false,
            },
          ],
          examples: [
            'jshow example',
            'jshow example --name "jshow"',
            'jshow ex -n "test"',
          ],
          validate: (options) => {
            // Optional: custom validation
            if (options.name && typeof options.name !== 'string') {
              return 'Name must be a string';
            }
            return null;
          },
        };
      }
    
      public async beforeExecute(context: CommandContext): Promise<void> {
        console.log(`Starting command: ${context.name}`);
      }
    
      public async execute(context: CommandContext): Promise<void> {
        const { options } = context;
        console.log(`Hello, ${String(options.name || 'world')}!`);
      }
    
      public async afterExecute(context: CommandContext): Promise<void> {
        console.log(`Command completed in ${Date.now() - context.startTime}ms`);
      }
    }
  3. Run the command

    # Development (runs src/cli.ts via ts-node from package root)
    pnpm start
    
    # Production-style: build then run the published bin (loads dist/cli.mjs with ts-node loader when needed)
    pnpm build
    pnpm exec jshow example --name "jshow"
    # or, if jshow is on PATH: jshow example --name "jshow"

Library usage

Compose the framework in your own Node process without cwd auto-discovery:

import { CommandProgram, initBuiltIn, BaseCommand, type CommandContext } from '@jshow/cli';

class DeployCommand extends BaseCommand {
  static key = 'deploy';
  protected get args() {
    return { name: 'deploy', description: 'Deploy service' };
  }
  async execute(ctx: CommandContext) {
    console.log(ctx.options);
  }
}

CommandProgram.use(DeployCommand);
await initBuiltIn(CommandProgram).run();
  • The package entry also re-exports all of commander, ./utils, and logger (see Utilities below).
  • runjShow / dist/cli.mjs are not exported from the main entry; use the bin or the built CLI module for full discovery.

Repository scripts

| Command | Description | | --- | --- | | pnpm build | Cleans dist/ / out/ then runs vite build (library + cli entry as dist/*.mjs / *.cjs) | | pnpm test | Runs vitest --run | | pnpm start | cd src && ts-node ./cli.ts — dev CLI against the current working directory | | pnpm cli | Runs bin/cli.mjs from test/fixtures/empty-cli-cwd (e.g. pnpm cli -- --help) | | pnpm clean | rm -rf ./dist && rm -rf ./out (Unix); on Windows use manual removal or Git Bash if rm is unavailable | | pnpm fix:all | Prettier + ESLint fix |


Environment variables

| Variable | Where used | Description | | --- | --- | --- | | JSHOW_CLI_MAX_DEPTH | src/cli.ts | Max directory depth when scanning for .cmd / .plugin files under process.cwd() (parsed as integer, minimum 2). | | JSHOW_CLI_IGNORE_NAMES | src/cli.ts | Comma-separated top-level directory names to skip while scanning (e.g. other packages in a monorepo). | | JSHOW_CLI_TS_RUNTIME | src/cli.ts | Set to 1 to treat the process as ts-node-capable and allow loading .cmd.ts / .plugin.ts during discovery. | | JSHOW_CLI_NO_TS_LOADER | bin/cli.mjs | Set to 1 to run dist/cli.mjs without the ts-node ESM loader (.ts discovery files are not loaded). | | TS_NODE_PROJECT / TS_NODE_COMPILER_OPTIONS / execArgv with ts-node | src/cli.ts, bin/cli.mjs | When set (or when the bin uses the ts-node loader), .ts discovery files may be loaded; otherwise only .js files are loaded from the workspace. |


Examples

The examples/ directory contains working examples:

TypeScript Examples

  • hello.cmd.ts – A simple Hello World command demonstrating basic command structure
  • greet.cmd.ts – A command with options, aliases, and validation
  • build.cmd.ts – A complex command using plugins and command grouping

CommonJS Examples

  • hello.cmd.js – A basic command example using CommonJS syntax
  • build.cmd.js – A command with plugins example using CommonJS syntax

Plugin Examples

  • logger.plugin.ts – A logging plugin with lifecycle hooks (priority: 50)
  • timer.plugin.ts – A timing plugin for performance monitoring (priority: 100)
  • error-handler.plugin.ts – An error handling plugin example (priority: 200)

See examples/README.md for detailed usage instructions.


Built-in commands

Registered automatically by initBuiltIn(CommandProgram) before CommandProgram.run():

  • release: interactive flow to pick public packages, bump versions (semver), run pnpm install, git add / git commit -F, and optional git push (multi-repo and monorepo can run in one invocation). End-of-run Report table includes status and count (selected packages). See docs/release.md.
  • publish: validates a single package, strips devDependencies, resolves workspace: / catalog: versions for publish, and runs npm publish (CI-oriented; leaves the formatted package.json on disk). See docs/publish.md.
  • backup: resolves packages via getGroupPackages (falls back to .git repo scan), optionally git pull per package, then copies each package’s top-level entries to an output folder (skips node_modules; -c excludes .git). See docs/backup.md.
  • upgrade: scans workspace dependencies, lets you multi-select dependencies to query, fetches registry versions, interactively confirms per-field bumps, writes package.json, runs pnpm install, and optional Git commit/push (multi-repo vs monorepo cannot be mixed). --force skips commit/push prompts. See docs/upgrade.md (--local not wired yet).

Implementations: src/built-in/commands/.


Utilities (re-exported utils)

Imported from @jshow/cli, shared with built-in release / backup:

| Area | Symbols | Purpose | | --- | --- | --- | | Workspace | getGroupPackages, getWorkspacePackages, separateGroupPackages | Scan monorepo / multi-package roots | | FS / process | existsSync, readJsonSync, writeJsonSync, execSync, cpSync, … | Safe I/O and sync subprocess | | Git | getCurrentBranch, pullCurrentBranch, getUnCommittedFiles, diffGit, addGit, commitGit, pushGit, … | Release/upgrade/backup Git helpers | | pnpm | installPnpm, readPnpmCatalogs, findPnpmWorkspaceRoot, PNPM_BUILT_IN_WORKSPACE, PNPM_BUILT_IN_CATALOG | Install deps, catalog read, workspace root lookup, built-in prefixes | | Prompts | confirmInquirer, inputInquirer, checkboxInquirer, … | Dynamic inquirer wrappers for CLI | | Terminal | red, green, yellow | ANSI color helpers | | Regexp | toRegExp, toPatterns | Comma-separated filters (e.g. backup -f, upgrade -i) |

See src/utils/index.ts and submodule JSDoc for the full surface.


API Documentation

Package entry (@jshow/cli)

The published "." export includes: everything from commander, CommandProgram, initBuiltIn, BaseCommand / BasePlugin and related types, isCommand / isPlugin, all symbols from ./utils, and the shared logger. The runnable CLI is the separate build target dist/cli.mjs (wired via bin/cli.mjs); it is not re-exported from the main entry to avoid importing the package accidentally starting a CLI.

CommandProgram

Singleton-style facade: holds the Commander root program, plugin list, and command registry.

Static properties

  • version: string — read from the package’s own package.json next to the built program module.
  • program: Command — Commander root; subcommands are mounted here in run().

Static methods

use(command: CommandClassType, force?: boolean): typeof CommandProgram

Registers a command class. Registration key is command.key if set, otherwise command.name (note: static name = 'foo' overrides Function.name).

install(plugin: PluginClassType, force?: boolean): typeof CommandProgram

Installs a plugin class; instances are sorted by ascending priority (smaller runs earlier).

reset(autoRun?: boolean): void

Clears plugins/commands and rebuilds the root Command (intended for tests; optional autoRun re-inits built-ins).

run(): Promise<void>

Mounts all commands, enhances help text, then await program.parseAsync(process.argv).

initBuiltIn

initBuiltIn(CommandProgram) installs default plugins and registers built-in commands (release, publish, backup, upgrade), returning CommandProgram for chaining.

CommandArgs

Command argument configuration interface.

Properties

  • name: string - Command name (required)
  • description?: string - Command description
  • aliases?: string[] - Command aliases
  • plugins?: string[] - List of plugin names to use for this command
  • group?: string - Command group for help organization
  • arguments?: CommandArgument[] — positional arguments (command.argument(...))
  • options?: CommandOption[] — Commander options (command.option(...))
  • examples?: string[] - Usage examples
  • validate?: (options: Record<string, unknown>) => string | null - Optional validation function that returns an error message or null

CommandOption

Command option configuration interface.

Properties

  • name: string - Option long name (used as --${name}), e.g. 'name' or 'verbose'
  • abbr?: string - Option short name (single char), e.g. 'n' for -n
  • flagValue?: boolean - When true, the option is declared with a value placeholder (--name <arg> style); when false, it is a boolean flag
  • description?: string - Option description
  • defaultValue?: T - Default value for the option
  • required?: boolean - Whether the option is required (default: false)
  • variadic?: boolean - Whether this option accepts multiple values (becomes --name <value...>)

BaseCommand

Command base class. All custom commands should extend this class.

Static properties

  • static key: string — preferred registration key (defaults to ''; may be filled from filename when auto-loading).
  • static force: boolean — allow replacing an existing registration (default false).
  • static name = 'subcommand' — optional; overrides Function.name and can be used as the registration key when key is empty.

Instance properties

  • key (getter): resolves static key, else constructor name, else args.name.
  • command: Command - Commander command instance (protected)

Abstract Methods

execute(context: CommandContext): Promise<void>

Command execution logic. Subclasses must implement this method.

Protected Methods

get args(): CommandArgs

Get command argument configuration. Subclasses must implement this getter.

The CommandArgs interface includes:

  • name: string - Command name
  • description?: string - Command description
  • aliases?: string[] - Command aliases
  • plugins?: string[] - List of plugin names to use for this command
  • group?: string - Command group for help organization
  • arguments? / options? — as above
  • examples?: string[] - Usage examples
  • validate?: (options: Record<string, unknown>) => string | null - Optional validation function
beforeExecute?(context: CommandContext): Promise<void>

Lifecycle hook executed before command execution.

afterExecute?(context: CommandContext): Promise<void>

Lifecycle hook executed after command execution.

onError(error: Error, context: CommandContext): boolean

Error handling hook. Returns true if error is handled, false otherwise.

BasePlugin

Plugin base class. All custom plugins should extend this class.

Static properties

  • static key: string — registration key (defaults to '').
  • static force: boolean — allow replacing an existing plugin registration.

Instance properties

  • key (getter): static key or constructor name.
  • priority: number - Plugin priority (default: 100, lower number = higher priority)

Methods

beforeExecute?(context: CommandContext): Promise<void>

Lifecycle hook executed before command execution.

afterExecute?(context: CommandContext): Promise<void>

Lifecycle hook executed after command execution.


File Naming Conventions

Command Files

TypeScript Files

  1. File naming: Must end with .cmd.ts
  2. Default export: Must use export default to export the command class
  3. Extend base class: Command class must extend BaseCommand
  4. Static identity: Set static key and/or static name (used with CommandProgram.use); filename-derived key is applied when auto-loading if missing
  5. Implement methods: Must implement execute(context) method and args getter

CommonJS Files

  1. File naming: Must end with .cmd.js
  2. Import dependencies: Use require() to import: const { BaseCommand } = require('@jshow/cli');
  3. Export class: Use module.exports to export class (Node.js automatically treats it as default export)
  4. Extend base class: Command class must extend BaseCommand
  5. Static identity: Set static key and/or static name
  6. Implement methods: Must implement execute(context) method and args getter

Plugin Files

TypeScript Files

  1. File naming: Must end with .plugin.ts
  2. Default export: Must use export default to export the plugin class
  3. Extend base class: Plugin class must extend BasePlugin
  4. Static identity: Set static key and/or static name

CommonJS Files

  1. File naming: Must end with .plugin.js
  2. Import dependencies: Use require() to import: const { BasePlugin } = require('@jshow/cli');
  3. Export class: Use module.exports to export class (Node.js automatically treats it as default export)
  4. Extend base class: Plugin class must extend BasePlugin
  5. Static identity: Set static key and/or static name

Auto Discovery

The CLI automatically scans the current working directory and its subdirectories, finds all .cmd.ts, .cmd.js, .plugin.ts, or .plugin.js files and loads them automatically.

Scanning rules

  • Starts at process.cwd() and recurses up to JSHOW_CLI_MAX_DEPTH (default 2, minimum 2).
  • Skips dot-prefixed dirs, common junk dirs (node_modules, etc., see isIgnoreDir), names listed in JSHOW_CLI_IGNORE_NAMES, and the package’s own built-in/commands tree when it appears under the scan path.
  • Only loads files ending with .cmd.ts, .cmd.js, .plugin.ts, or .plugin.js.
  • .ts files are only considered when a ts-node-style runtime is detected; otherwise only .js files load.
  • If the class has no static key, the basename (without .cmd / .plugin) is assigned to key before registration.

Development notes

  • Library vs CLI: import @jshow/cli for CommandProgram / BaseCommand / utils; run jshow (or pnpm start in this repo) for cwd auto-discovery. Entry points: src/index.ts (library), src/cli.tsdist/cli.mjs (CLI). runjShow is exported from src/cli.ts for tests/custom wrappers but not from the package main entry.
  • Discovery resilience: a broken .cmd / .plugin in the workspace logs a warning only so --help and built-ins still run (loadCommand / loadPlugin).
  • Boolean invert: for flagValue: false options, invert: true also registers --no-<name> (used by built-in backup -c, release --check / --push, etc.); see initOption in src/command.ts.
  • Built-in commands: registered in src/built-in/commands/index.ts; see docs/*.md for flow details and this README for a summary.
  • JSDoc: public and internal helpers in src/ are documented next to implementations—prefer source JSDoc over duplicating API lists here.

Development Workflow

  1. pnpm install – Install dependencies
  2. Create command files (.cmd.ts or .cmd.js) or plugin files (.plugin.ts or .plugin.js) in your project
  3. pnpm start – Run in development mode
  4. pnpm build – Build for production
  5. Test your commands with jshow <command>

Directory Layout

├── bin/cli.mjs         # Published bin: Node + ts-node loader → dist/cli.mjs
├── src/
│   ├── cli.ts          # Runnable CLI (scan cwd, initBuiltIn, parseAsync)
│   ├── index.ts        # Library entry (re-exports commander + framework + utils + logger)
│   ├── command.ts      # BaseCommand & option/argument types
│   ├── plugin.ts       # BasePlugin
│   ├── program.ts      # CommandProgram, initBuiltIn
│   ├── logger.ts       # Shared logger fork
│   ├── built-in/       # Default commands/plugins wired by initBuiltIn
│   └── utils/          # Workspace scan, git, pnpm, fs helpers
├── test/               # Vitest specs and fixtures (not published)
├── docs/               # Built-in command docs (backup / publish / release / upgrade)
├── examples/           # Sample .cmd / .plugin files
├── scripts/            # Dev helpers (e.g. run-cli-help.mjs)
├── dist/               # Vite build output (gitignored)
└── ...

License

MIT © jShow


Questions or issues? Open an issue at https://github.com/j-show/cli/issues.