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 🙏

© 2025 – Pkg Stats / Ryan Hefner

seeli

v16.0.0

Published

Object oriented, flexible CLI tools

Downloads

551

Readme

Test + Release package dependancies Codacy Badge semantic-release

seeli ( C. L. I. )

Object orientated, event driven , Interactive CLI module. Seeli aims to give you the tools to compose A command line interface the way you want it, and otherwise, stays out of your way.

gif

const os = require('os')
const cli = require('seeli')

cli.config({
  exitOnError: true
, color: 'green'
, name: 'hello'
})

const Hello = new cli.Command({
  description:"displays a simple hello world command"
, name: 'world'
, ui: 'dots'
, usage: [
    `${cli.bold("Usage:")} ${cli.config('name')} world --interactive`
  , `${cli.bold("Usage:")} ${cli.config('name')} world --name=john`
  , `${cli.bold("Usage:")} ${cli.config('name')} world --name=john --name=marry --name=paul -v screaming`
  ]

, flags: {
    name: {
      type: [ String, Array ]
    , shorthand: 'n'
    , description: 'The name of the person to say hello to'
    , required: true
    }

  , 'nested:value' : {
      type: Number
    , shorthand: 'nv'
    , description: 'A nested value'
    , name: 'nested'
    }

  , excited: {
      type:Boolean
    , shorthand: 'e'
    , description: 'Say hello in a very excited manner'
    , default:false
    }

  , volume:{
      type: String
    , choices: ['normal', 'screaming']
    , description: 'Will yell at each person'
    , default: 'normal'
    , shorthand: 'v'
    }
  }
, onContent: (content) => {
    // command success
    // content is the final output from run function
    // If a string is returned by `run`, it will print automatically. If it's a data
    // structure, then this method can be used to display it however.

    console.log(content.join(os.EOL))
  }

, run: async function(cmd, data) {
    const out = []
    this.ui.start('processing names')
    var names = Array.isArray( data.name ) ? data.name : [ data.name ]
    for (var x = 0; x < names.length; x++) {
      this.ui.text = (`processing ${names[x]}`)
      await new Promise((resolve) => {
        setTimeout(() => {
          let value = "Hello, " + names[x]
          if( data.excited ){
            value += '!'
          }

          out.push(data.volume === 'screaming' ? value.toUpperCase() : value)
          resolve(true)
        }, 1000 * x + 1)
      })
    }

    this.ui.succeed('names processed successfully')
    // Anything returned from run is emitted from the `content` event.
    // Strings will automatically be written to stdout.
    return out
  }
})

cli.use(Hello)
cli.run()

now you will have a fully functional hello world command with help and an interactive walk through

node ./cli help world
node ./cli world --help
node ./cli world --interactive
node ./cli world --name=Mark --name=Sally --no-excited

API

Seeli.Command(<Command>)

A constructor for creating a command, including its flags and run method to be executed by Seeli.run(). See Command Options on how to configure a command.

Command Options

name | type | default | description -----|:-----:|--------|------------- description | String | "" | Used to render help output strict | Boolean | false | When true, commands will error when the receive unknown flags args | Array | null | if supplied, agrs will be used instead of process.argv interactive | Boolean | true | If set to false, the command will not offer interactive mode usage | String / Array | "" | A string or array of strings used to generate help text flags | Object | {} | key value pairs used to control the command where keys are the name of the flag and the values is a configuration object for the flag requires_one | Array | undefined | Specify the flag names where one of their values must be entered ui | String | dots | The kind of progress indicator your command should use. After instantiation, this becomes an instance of ora used to print output. run | Function | no-op | An async function used as the body of the command. It will be passed a subcommand name if one was passed, and a data object containing the processed values from the command input. commands | Command[] | [] | A list of additional command to utilize as sub commands.

Flag Options

name | required | type | description -----|:--------:|:----:|------------ type | true | string |The type of input that is expected. Boolean types to not expect input. The present of the flag implies true. Additionally, boolean flags allow for --no-<flag> to enforce false. If you want to accept multiple values, you specify type as an array with the first value being the type you which to accept. For example [String, Array ]**** means you will accept multiple string values.| description | false | string | A description of the flag in question. | required | false | boolean | If set to true a RequiredFieldError will be emitted | shorthand | false | string | An options short hand flag that will be expanded out to the long hand flag. | interactive | false | boolean | If set to false the flag will omitted from interactive prompts default | false | mixed | A value to return if the flag is omitted. | mask | false | boolean | interactive mode only Sets the input type to masked input to hide values choices | false | array | Used only during an interactive command. Restricts the users options only to the options specified | multi | false | boolean | interactive mode only If choices is specified, and multi is true, this user will be presented a multi checkbox UI allowing them to pick multiple values. The return value will be an array skip | false | boolean | interactive mode only - if set to true this flag will be omitted from the interactive command prompts | event | false | boolean | If set to true the command will emit an event withe the same name as the flag with the value that was captured for that flag | when | false | function | interactive mode only Receives the current user answers hash and should return true or false depending on whether or not this question should be asked. validate | false | function | A synchronous function that receives the command object, which should return true or undefined if the value is valid. Otherwise, an error message (String) can be returned, and it will be rendered. If false is returned, a default error message is provided. Note: These are called in interactive mode as well, during which time, other flag values may not yet be present. filter | false | function | Receives the user input and return the filtered value to be used inside the program. The value returned will be added to the Answers hash. required_with | false | Array | A non-empty array which says that if the flag is set, then the specified other flags must also be set, i.e. "mutual inclusion." required_without | false | Array | A non-empty array which says that if the flag is set, then none of the other specified flags may also be set, i.e. "mutual exclusion."

Nested Flags

Flag names that contain a colon (:) will be parsed as a nested value in the data that is return to you commands. You can Set arbitrarily deep values. You can use this to automatically construct complex object. Array values are limited to primitive types

// cli --foo:bar:foobar=hello --foo:bar:baz=world --nested:array=1 --nested:array=2

{
  foo: {
    bar: {
      foobar: "hello"
    , baz: "world"
    }
  }
, nested: {
    array: [1, 2]
  }
}

cmd Shape

Functions will often be passed the cmd object for use inside the function. This object contains all of the user-inputted flags as well as internal fields from raw argument parsing (which can largely be ignored by the user).

Example:

const fs = require('fs')

// ...snip

, validate: (cmd) => {
    console.log('cmd contents', cmd)

    // Clear the output file before each run
    if (cmd.output_file) {
      fs.rm(cmd.output_file, {force: true}, () => {})
    }
  }

Output:

cmd contents {
  my_param1: 'hello',
  my_param2: 'goodbye',
  output_file: '/tmp/output.txt',
  my_optional_param: undefined
  argv: {
    remain: [<internal flag processing>],
    cooked: [<internal flag processing>],
    original: [<internal flag processing>]
  },
  interactive: false,
  color: true,
}

Seeli.run( )

Executes The command line interface

Seeli.list<Array>

List of all top level registered commands

Seeli.use( [name <string>,] cmd <Command> )

Registers a new command where the Command's name will invoke the associated command. Optionally, a different name can be passed as the first parameter which would override the value of Command.name.

const cli = require('seeli')
const UnnamedCmd = new cli.Command()
const NamedCommand = new cli.Command({name: 'my_command'})

cli.use('test', UnnamedCmd)
cli.use(NamedCommand)
cli.run()

Seeli.bold( text <string>)

Wraps text in the ansi code for bold.

Seeli.green( text <string>)

Wraps text in the ansi code for green.

Seeli.blue( text <string>)

Wraps text in the ansi code for blue.

Seeli.red( text <string>)

Wraps text in the ansi code for red.

Seeli.yellow( text <string>)

Wraps text in the ansi code for yellow.

Seeli.cyan( text <string>)

Wraps text in the ansi code for cyan.

Seeli.magenta( text <string>)

Wraps text in the ansi code for magenta.

Seeli.redBright( text <string>)

Wraps text in the ansi code for redBright.

Seeli.blueBright( text <string>)

Wraps text in the ansi code for blueBright.

Seeli.greenBright( text <string>)

Wraps text in the ansi code for greenBright.

Seeli.yellowBright( text <string>)

Wraps text in the ansi code for yellowBright.

Seeli.cyanBright( text <string>)

Wraps text in the ansi code for cyanBright.

Seeli.config( key <string>, value <object> )

Sets a single configuration value (see Config Options).

Seeli.config( opts <object> )

Set multiple configuration values using a single object.

Config Options

  • color <String> - The chalk color to use when outputting help text. default green
  • name <String> - the name of the primary command that is used in generated help. If this is not set, the filename is used.
  • exitOnError <Boolean> - Seeli will forcefully exit the current process when an error is encountered. default false
  • exitOnContent <Boolean> - Seeli will forefully exit the current process when it is passed output content from a command. default true
  • plugins <String>|<function>[] - A list of plugins to load and execute. A plugin may be either a function, or a module id to be required. If it is a module id, the module must export a single function which will be passed the seeli instance when called.
  • help <String> - a file path or module name to a custom help command. This will be passed to require and must export a single command instance
    • seeli.config('help', '/path/to/help/command')

Seeli.config( key <string> )

A config value to look up. Can be a dot separated key to look up nested values

Package Configuration

Alternatively, initial configuration may be provided via package.json in a top-level key - seeli. However, this is mostly for importing plugin packages that are published to a registry.

{
  "seeli": {
    "color": "blue",
    "name": "whizbang",
    "plugins": [
      "@myscope/simple-command"
    ]
  }
}

Auto Help

Seeli will generate help from the usage string and flags. You can help as a command seeli help <command> or as a flag seeli <command> --help

Asyncronous

Your defined run function can be an async function, or a function that returns a Promise. This allows you to do complex async operations and I/O. If an error is thrown, it will be displayed. Otherwise, the content returned from your run function will be output to stdout ( if it returned a String).

Showing Progress

Your command's run function has access to an instance of ora available in this.ui, allowing you to display progress indicators and helpful messages while you perform other work. Optionally, if this is out of scope, you may also use the instance directly from seeli. See the README in the ora repository for more info.

const cli = require('seeli')

// Inside of Command
this.ui.fail('Some error message')
this.ui.text('Update the status bar')
this.ui.info('Informational text here')

// Outside of Command
function something() {
  cli.ui.success('My function was called!')
}

Events

Instances of the seeli Command or Commands that inherit from it as also instances of the EventEmitter class. By default any flag that has its event option set to true will emit an event with the value of the flag before the run function is executed.

var EventCommand = new cli.Command({
  args:[ '--one', '--no-two']
, flags:{
    one:{
      type:Boolean
    , event:true
    }
  , two:{
      type:Boolean
    , event:true
    }
  }
, run: async function( cmd, data ){
    return data.one && data.two
  }
});

EventCommand.on('one', function( value ){
  assert.equal( true, value );
});

EventCommand.on('two', function( value ){
  assert.equal( false, value )
});

EventCommand.on('content', function( value ){
  assert.equal( false, value );
});

EventCommand.run( null );