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

@spinajs/cli

v2.0.507

Published

SpinaJS command line module

Readme

@spinajs/cli

SpinaJS command line module. Build CLI commands as DI-managed classes using decorators — with full access to SpinaJS features (dependency injection, logging, configuration, intl) inside your command code.

Built on top of commander.

Installation

npm install @spinajs/cli

Examples

Runnable examples for each feature set live in examples/ — see examples/README.md for the full list:

Writing a command

A command is a class extending CliCommand, decorated with @Command and (optionally) @Argument / @Option. The execute method receives the parsed arguments in declaration order, followed by an options object.

import { Log, Logger } from '@spinajs/log';
import { CliCommand, Command, Argument, Option } from '@spinajs/cli';

interface GreetOptions {
  loud: boolean;
}

@Command('greet', 'Greets a user')
@Argument('name', true, 'who to greet')
@Option('-l, --loud', false, 'shout the greeting', false)
export class GreetCommand extends CliCommand {
  @Logger('greet')
  protected Log: Log;

  public async execute(name: string, options: GreetOptions): Promise<void> {
    const msg = `Hello, ${name}!`;
    this.Log.info(options.loud ? msg.toUpperCase() : msg);
  }
}

Decorators

Each decorator has a positional form (simple cases) and an object form (full configuration). The two are equivalent:

// positional
@Option('-t, --timeout <ms>', true, 'request timeout', undefined, (v) => parseInt(v))

// object form — unlocks choices / env / configKey
@Option({ flags: '-t, --timeout <ms>', required: true, description: 'request timeout', parser: (v) => parseInt(v) })

| Decorator | Positional signature | | --- | --- | | @Command | (nameAndArgs, description, opts?) | | @Argument | (name, required, description?, defaultValue?, parser?) | | @Option | (flags, required?, description?, defaultValue?, parser?) |

@Command object form{ nameAndArgs, description, ... }:

| Field | Effect | | --- | --- | | aliases | alternate names the command can be invoked by | | summary | short text shown in the parent command list | | usage | custom usage string | | parent | nest this command under another (matched by the parent's first name token) | | allowUnknownOption / allowExcessArguments | relax parsing | | passThroughOptions / enablePositionalOptions | positional/pass-through option handling | | helpText | extra help sections — [{ position, text }] (commander addHelpText) | | configureHelp | customize help formatting (commander configureHelp) |

@Argument object form{ name, required, ... }:

| Field | Effect | | --- | --- | | choices | restrict the value to an enum set (validated) | | variadic | collect multiple values into an array (name...) | | defaultValue / parser | as in the positional form |

@Option object form{ flags, required, ... }:

| Field | Effect | | --- | --- | | choices | restrict the value to an enum set (validated) | | env | read the value from an environment variable when not passed | | configKey | fall back to a @spinajs/configuration value as the default | | conflicts | option name(s) this option cannot be combined with | | implies | other option values implied when this option is set | | preset | value used for an optional-value option (--x [val]) given without a value | | defaultValue / parser | as in the positional form |

Negatable (--no-sauce) and optional-value (--cheese [type]) options need no extra fields — express them in the flags string; commander handles the rest.

Notes:

  • required arguments render as <name>, optional ones as [name]; variadic appends ....
  • A parser converts the raw string value, e.g. (v) => parseInt(v).
  • A required @Option cannot have a default value (incl. configKey) — it is ignored with a warning.

Subcommands

Nest commands by giving a child the parent's name via parent:

@Command({ nameAndArgs: 'db', description: 'database commands' })
export class DbCommand extends CliCommand { /* ... */ }

@Command({ nameAndArgs: 'migrate', description: 'run migrations', parent: 'db' })
export class DbMigrateCommand extends CliCommand { /* ... */ }
// invoked as:  spinajs db migrate

Lifecycle hooks

Override onCreation, onPreAction, or onPostAction to tap into the command lifecycle. onCreation hands you the raw commander Command for anything the decorators don't cover:

export class GreetCommand extends CliCommand {
  public onCreation(command: CommanderCommand): void {
    command.addHelpText('after', '\nExample: spinajs greet World');
  }

  public onPreAction(): void { /* runs before execute */ }
  public onPostAction(): void { /* runs after execute */ }

  public async execute(): Promise<void> { /* ... */ }
}

Using commander directly

The commander surface is re-exported from @spinajs/cli so you don't need a direct dependency on commander. The Command/Option/Argument classes are aliased (CommanderCommand, CommanderOption, CommanderArgument) to avoid colliding with the decorators of the same name.

Registering commands

Commands are auto-discovered from the directories listed under the system.dirs.cli configuration key:

{
  system: {
    dirs: {
      cli: [ '/abs/path/to/commands' ],
    },
  },
}

Running

The package ships a spinajs bin:

spinajs greet World --loud
spinajs --version
spinajs --help

The following framework options are consumed by @spinajs/configuration (not by your commands) and are stripped before parsing:

| Option | Purpose | | --- | --- | | --app <name> | select the running app | | --env <name> | select the environment | | --apppath <path> | override the app base directory |

Framework integration

The wrapper wires commander into the rest of SpinaJS:

  • Logging — commander's error output (and after-error help) is routed through @spinajs/log (Log.error); --help/--version still print to stdout. Unknown commands/options get "did you mean" suggestions.
  • Configuration — options can fall back to a config value via configKey, and framework options (--app/--env/--apppath) are consumed by @spinajs/configuration and stripped before parsing.
  • Internationalization — when @spinajs/intl is present and configured, command/option/argument descriptions are translated through its global __. This is optional: without intl, text is used as-is (no hard dependency).
  • Versioning--version reports the installed @spinajs/cli version.

Built-in commands

| Command | Description | | --- | --- | | configuration:dump [-p, --path <path>] | Dumps the resolved configuration (optionally a sub-path) |