SEELI: Object Orientated CLI framework for Node.js

C

ommand line tools are a difficult thing to get right. I've never really been too happy with the more popular ones in the node.js community. Nothing supplied all of the basic features or implementation style that I wanted. Most of them try too hard to be the interface when all I really want is something that is really good at parsing flags and options, and return input in a plain object. So I wrote my own. Let me introduce you to Seeli ( C-L-I ), and object orientated, evented, and classy framework for building elegant Command line interfaces:

Installation

npm install seeli --save  

Object Orientated

To create a new command, all you need to do is create a new instance of a seeli.Command.

var cli = require("seeli")  
var Hello;  
Hello = new cli.Command({  
    description:"Say Hello"
    ,run: function( cmd, data, done ){
        done( null, "Hello")
    }
});

// run it.
Hello.run() // "Hello"  

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. Help is generated from the Usage options and each of the description options from each of the descriptions of the defined flags so you don't have to jump through hoops to get custom help output from your commands.

var cli = require("seeli");  
var Hello;  
Hello = new cli.Command({  
  description:"Say Hello"
  ,usage:[
    cli.bold("Usage:") + " cli hello --interactive",
    cli.bold("Usage:") + " cli hello --name=john",
    cli.bold("Usage:") + " cli hello --name=john --name=marry --name=paul -v screaming"
  ]
  ,run: function( cmd, data, done ){
    done( null, "Hello")
  }
});

Intuitive

Seeli simple, human readable JSON syntax to define input parameters and plain functions that you write to dictate how it should behave. For example, If we want our hello command to accept a name parameter, that is a string, and also required - we would have to add a name property under then flags option.

var cli = require("seeli");  
var Hello;  
Hello = new cli.Command({  
    description:"Say Hello"
    ,flags:{
        name:{ 
            type:String 
           ,required:true
           ,description:"The name of the person you wish to say hello to"
        }
    }
    ,run: function( cmd, data, done ){
        done( null, "Hello " + data name );
    }
});

Evented

Instances of the seeli Command or Commands the 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 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: function( cmd, data, done ){
    done( null, data.one && data.two )
  }
});

// listen for the event when the one flag is parsed
EventCommand.on('one', function( value ){  
  assert.equal( true, value );
});

// listen for the event when the two flag is parsed
EventCommand.on('two', function( value ){  
  assert.equal( false, value )
});

// listen for the event when the command has completed
EventCommand.on('content', function( value ){  
  assert.equal( false, value );
});

//Run it!
EventCommand.run( null );  

Interactive

Although CLI tools are great, as they become more and more mature, they tend to have so many flags and options it become difficult to keep track of which flags are optional, what types of input they take, if they are expecting specific parameters, etc. It also make the barrier to entry for people trying to use your tools that much harder. So to help solve that, seeli introduces an interactive mode where it steps end users through each of the parameters until the have entered in the necessary amount of data to run the command.

Flexible

The non destructive nature and object oriented design of seeli allows you to use the commands from anywhere in your applications for what ever you what. They do not have to write to stdout if you don't need them to do that. Under the hood, Each command processes a list of arguments. If none are supplied, it will read process.argv. It converts all of the input into a plain javascript object and passes that object to the run function, which you define. Commands can even call other commands. Because each one parses and handles arguments independent of one another, there is no chance of parameter clobbering

Of course if you are looking for a straight forward terminal style command line interface, you can register your commands with the seeli module, using the use method and it will register them for use in a terminal. More over, it will alias all of your commands to shorthand abbreviations to help catch typos and cut a few keystrokes.

cli npm packages terminal node.js