{"id":13532389,"url":"https://github.com/esatterwhite/node-seeli","last_synced_at":"2026-04-02T22:10:17.202Z","repository":{"id":18365205,"uuid":"21545470","full_name":"esatterwhite/node-seeli","owner":"esatterwhite","description":"Object orientated, event driven CLI module","archived":false,"fork":false,"pushed_at":"2025-03-05T19:53:35.000Z","size":9136,"stargazers_count":20,"open_issues_count":7,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-12T07:03:02.443Z","etag":null,"topics":["ansi","command-line","interactive","javascript","terminal"],"latest_commit_sha":null,"homepage":"http://esatterwhite.github.io/node-seeli/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/esatterwhite.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-07-06T18:05:21.000Z","updated_at":"2025-03-10T05:13:06.000Z","dependencies_parsed_at":"2024-01-14T02:22:19.184Z","dependency_job_id":"f160d4b5-3f9a-4b20-a79f-efb478a41e69","html_url":"https://github.com/esatterwhite/node-seeli","commit_stats":{"total_commits":419,"total_committers":9,"mean_commits":46.55555555555556,"dds":"0.18615751789976132","last_synced_commit":"755109f57408b59209b516252ac987817a35cb44"},"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esatterwhite%2Fnode-seeli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esatterwhite%2Fnode-seeli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esatterwhite%2Fnode-seeli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esatterwhite%2Fnode-seeli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esatterwhite","download_url":"https://codeload.github.com/esatterwhite/node-seeli/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244189829,"owners_count":20412991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansi","command-line","interactive","javascript","terminal"],"created_at":"2024-08-01T07:01:10.619Z","updated_at":"2026-04-02T22:10:17.191Z","avatar_url":"https://github.com/esatterwhite.png","language":"JavaScript","funding_links":[],"categories":["[JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)"],"sub_categories":["Useful awesome list for Go cli"],"readme":"[![Test + Release](https://github.com/esatterwhite/node-seeli/actions/workflows/release.yml/badge.svg)](https://github.com/esatterwhite/node-seeli/actions/workflows/release.yml)\n[![package dependancies](https://img.shields.io/librariesio/github/esatterwhite/node-seeli.svg)](https://github.com/esatterwhite/node-seeli)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/0df91a318dc444e7aedcdd4c77fda673)](https://app.codacy.com/gh/esatterwhite/node-seeli/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\n# seeli ( C. L. I. )\n\nObject orientated, event driven , **Interactive** CLI module. Seeli aims to give you the tools to compose\nA command line interface the way you want it, and otherwise, stays out of your way.\n\n![gif](https://raw.githubusercontent.com/esatterwhite/node-seeli/master/assets/seeli.gif \"interactive mode\")\n\n```js\nconst os = require('os')\nconst cli = require('seeli')\n\ncli.config({\n  exitOnError: true\n, color: 'green'\n, name: 'hello'\n})\n\nconst Hello = new cli.Command({\n  description:\"displays a simple hello world command\"\n, name: 'world'\n, ui: 'dots'\n, usage: [\n    `${cli.bold(\"Usage:\")} ${cli.config('name')} world --interactive`\n  , `${cli.bold(\"Usage:\")} ${cli.config('name')} world --name=john`\n  , `${cli.bold(\"Usage:\")} ${cli.config('name')} world --name=john --name=marry --name=paul -v screaming`\n  ]\n\n, flags: {\n    name: {\n      type: [ String, Array ]\n    , shorthand: 'n'\n    , description: 'The name of the person to say hello to'\n    , required: true\n    }\n\n  , 'nested:value' : {\n      type: Number\n    , shorthand: 'nv'\n    , description: 'A nested value'\n    , name: 'nested'\n    }\n\n  , excited: {\n      type:Boolean\n    , shorthand: 'e'\n    , description: 'Say hello in a very excited manner'\n    , default:false\n    }\n\n  , volume:{\n      type: String\n    , choices: ['normal', 'screaming']\n    , description: 'Will yell at each person'\n    , default: 'normal'\n    , shorthand: 'v'\n    }\n  }\n, onContent: (content) =\u003e {\n    // command success\n    // content is the final output from run function\n    // If a string is returned by `run`, it will print automatically. If it's a data\n    // structure, then this method can be used to display it however.\n\n    console.log(content.join(os.EOL))\n  }\n\n, run: async function(cmd, data) {\n    const out = []\n    this.ui.start('processing names')\n    var names = Array.isArray( data.name ) ? data.name : [ data.name ]\n    for (var x = 0; x \u003c names.length; x++) {\n      this.ui.text = (`processing ${names[x]}`)\n      await new Promise((resolve) =\u003e {\n        setTimeout(() =\u003e {\n          let value = \"Hello, \" + names[x]\n          if( data.excited ){\n            value += '!'\n          }\n\n          out.push(data.volume === 'screaming' ? value.toUpperCase() : value)\n          resolve(true)\n        }, 1000 * x + 1)\n      })\n    }\n\n    this.ui.succeed('names processed successfully')\n    // Anything returned from run is emitted from the `content` event.\n    // Strings will automatically be written to stdout.\n    return out\n  }\n})\n\ncli.use(Hello)\ncli.run()\n```\n\nnow you will have a fully functional `hello world` command with help and an interactive walk through\n\n```\nnode ./cli help world\nnode ./cli world --help\nnode ./cli world --interactive\nnode ./cli world --name=Mark --name=Sally --no-excited\n```\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [API](#api)\n    * [Seeli.Command(`\u003cCommand\u003e`)](#seelicommandcommand)\n        * [Command Options](#command-options)\n        * [Flag Options](#flag-options)\n        * [Nested Flags](#nested-flags)\n        * [`cmd` Shape](#cmd-shape)\n    * [Seeli.run( )](#seelirun-)\n    * [Seeli.list`\u003cArray\u003e`](#seelilistarray)\n    * [Seeli.use( [name `\u003cstring\u003e`,] cmd `\u003cCommand\u003e` )](#seeliuse-name-string-cmd-command-)\n    * [Seeli.bold( text `\u003cstring\u003e`)](#seelibold-text-string)\n    * [Seeli.green( text `\u003cstring\u003e`)](#seeligreen-text-string)\n    * [Seeli.blue( text `\u003cstring\u003e`)](#seeliblue-text-string)\n    * [Seeli.red( text `\u003cstring\u003e`)](#seelired-text-string)\n    * [Seeli.yellow( text `\u003cstring\u003e`)](#seeliyellow-text-string)\n    * [Seeli.cyan( text `\u003cstring\u003e`)](#seelicyan-text-string)\n    * [Seeli.magenta( text `\u003cstring\u003e`)](#seelimagenta-text-string)\n    * [Seeli.redBright( text `\u003cstring\u003e`)](#seeliredbright-text-string)\n    * [Seeli.blueBright( text `\u003cstring\u003e`)](#seelibluebright-text-string)\n    * [Seeli.greenBright( text `\u003cstring\u003e`)](#seeligreenbright-text-string)\n    * [Seeli.yellowBright( text `\u003cstring\u003e`)](#seeliyellowbright-text-string)\n    * [Seeli.cyanBright( text `\u003cstring\u003e`)](#seelicyanbright-text-string)\n    * [Seeli.config( key `\u003cstring\u003e`, value `\u003cobject\u003e` )](#seeliconfig-key-string-value-object-)\n    * [Seeli.config( opts `\u003cobject\u003e` )](#seeliconfig-opts-object-)\n        * [Config Options](#config-options)\n    * [Seeli.config( key `\u003cstring\u003e` )](#seeliconfig-key-string-)\n* [Package Configuration](#package-configuration)\n* [Auto Help](#auto-help)\n* [Asyncronous](#asyncronous)\n* [Showing Progress](#showing-progress)\n* [Events](#events)\n\n\u003c!-- vim-markdown-toc --\u003e\n# API\n\n## Seeli.Command(`\u003cCommand\u003e`)\n\nA constructor for creating a command, including its flags and `run` method to be executed by `Seeli.run()`.  See [Command Options](#command-options) on how to configure a command.\n\n### Command Options\n\nname | type | default | description\n-----|:-----:|--------|-------------\n**description** | `String` |  `\"\"` | Used to render help output\n**strict** | `Boolean` |  `false` | When true, commands will error when the receive unknown flags\n**args** | `Array` | `null` | if supplied, `agrs` will be used instead of `process.argv`\n**interactive** | `Boolean` | `true` | If set to false, the command will not offer interactive mode\n**usage** | `String` / `Array` | `\"\"` | A string or array of strings used to generate help text\n**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\n**requires_one** | `Array` | `undefined` | Specify the flag names where one of their values must be entered\n**ui** | `String` | `dots` | The kind of [progress indicator](https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json) your command should use. After instantiation, this becomes an instance of [ora](https://www.npmjs.com/package/ora) used to print output.\n**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.\n**commands** | `Command[]` | `[]` | A list of additional command to utilize as sub commands.\n\n### Flag Options\nname | required | type | description\n-----|:--------:|:----:|------------\n**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-\u003cflag\u003e` 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.|\n**description** | `false` | `string` |  A description of the flag in question.  |\n**required** | `false` | `boolean` |  If set to `true` a `RequiredFieldError` will be emitted  |\n**shorthand**  | `false` | `string` | An options short hand flag that will be expanded out to the long hand flag. |\n**interactive** | `false` | `boolean` | If set to false the flag will omitted from interactive prompts\n**default**    | `false` | `mixed` | A value to return if the flag is omitted. |\n**mask**       | `false` | `boolean` | **interactive mode only** Sets the input type to masked input to hide values\n**choices**    | `false` | `array` | Used only during an interactive command. Restricts the users options only to the options **specified** |\n**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\n**skip**       | `false` | `boolean` | **interactive mode only** - if set to `true` this flag will be omitted from the interactive command prompts |\n**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 |\n**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.\n**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.\n**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.\n**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.\"\n**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.\"\n\n### Nested Flags\n\nFlag 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.\nYou can use this to automatically construct complex object. Array values are limited to primitive types\n\n```javascript\n// cli --foo:bar:foobar=hello --foo:bar:baz=world --nested:array=1 --nested:array=2\n\n{\n  foo: {\n    bar: {\n      foobar: \"hello\"\n    , baz: \"world\"\n    }\n  }\n, nested: {\n    array: [1, 2]\n  }\n}\n```\n\n### `cmd` Shape\n\nFunctions 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).\n\nExample:\n\n```js\nconst fs = require('fs')\n\n// ...snip\n\n, validate: (cmd) =\u003e {\n    console.log('cmd contents', cmd)\n\n    // Clear the output file before each run\n    if (cmd.output_file) {\n      fs.rm(cmd.output_file, {force: true}, () =\u003e {})\n    }\n  }\n```\n\nOutput:\n\n```bash\ncmd contents {\n  my_param1: 'hello',\n  my_param2: 'goodbye',\n  output_file: '/tmp/output.txt',\n  my_optional_param: undefined\n  argv: {\n    remain: [\u003cinternal flag processing\u003e],\n    cooked: [\u003cinternal flag processing\u003e],\n    original: [\u003cinternal flag processing\u003e]\n  },\n  interactive: false,\n  color: true,\n}\n```\n## Seeli.run( )\n\nExecutes The command line interface\n\n## Seeli.list`\u003cArray\u003e`\n\nList of all top level registered commands\n\n## Seeli.use( [name `\u003cstring\u003e`,] cmd `\u003cCommand\u003e` )\n\nRegisters a new command where the Command's `name` will invoke the associated command.\nOptionally, a different `name` can be passed as the first parameter which would override\nthe value of `Command.name`.\n\n```js\nconst cli = require('seeli')\nconst UnnamedCmd = new cli.Command()\nconst NamedCommand = new cli.Command({name: 'my_command'})\n\ncli.use('test', UnnamedCmd)\ncli.use(NamedCommand)\ncli.run()\n```\n\n\n## Seeli.bold( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for bold.\n\n## Seeli.green( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for green.\n\n## Seeli.blue( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for blue.\n\n## Seeli.red( text `\u003cstring\u003e`)\nWraps text in the ansi code for red.\n\n## Seeli.yellow( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for yellow.\n\n## Seeli.cyan( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for cyan.\n\n## Seeli.magenta( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for magenta.\n\n## Seeli.redBright( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for redBright.\n\n## Seeli.blueBright( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for blueBright.\n\n## Seeli.greenBright( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for greenBright.\n\n## Seeli.yellowBright( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for yellowBright.\n\n## Seeli.cyanBright( text `\u003cstring\u003e`)\n\nWraps text in the ansi code for cyanBright.\n\n## Seeli.config( key `\u003cstring\u003e`, value `\u003cobject\u003e` )\n\nSets a single configuration value (see [Config Options](#config-options)).\n\n## Seeli.config( opts `\u003cobject\u003e` )\n\nSet multiple configuration values using a single object.\n\n### Config Options\n\n* **color** `\u003cString\u003e` - The chalk color to use when outputting help text. default `green`\n* **name** `\u003cString\u003e`   - the name of the primary command that is used in generated help. If this is not set, the filename is used.\n* **exitOnError** `\u003cBoolean\u003e` - Seeli will forcefully exit the current process when an error is encountered. default `false`\n* **exitOnContent** `\u003cBoolean\u003e` - Seeli will forefully exit the current process when it is passed output content from a command. default `true`\n* **plugins** `\u003cString\u003e|\u003cfunction\u003e[]` - A list of plugins to load and execute. A plugin may be either a function, or a module id to be required.\n  If it is a module id, the module must export a single function which will be passed the seeli instance when called.\n* **help** `\u003cString\u003e`  - a file path or module name to a custom help command. This will be passed to `require` and must export a single command instance\n    * `seeli.config('help', '/path/to/help/command')`\n\n## Seeli.config( key `\u003cstring\u003e` )\n\nA config value to look up. Can be a dot separated key to look up nested values\n\n\n# Package Configuration\n\nAlternatively, initial configuration may be provided via `package.json` in a top-level key - `seeli`.\nHowever, this is mostly for importing plugin packages that are published to a registry.\n\n```json\n{\n  \"seeli\": {\n    \"color\": \"blue\",\n    \"name\": \"whizbang\",\n    \"plugins\": [\n      \"@myscope/simple-command\"\n    ]\n  }\n}\n```\n\n# Auto Help\n\nSeeli will generate help from the usage string and flags. You can help as a command `seeli help \u003ccommand\u003e` or as a flag `seeli \u003ccommand\u003e --help`\n\n# Asyncronous\n\nYour 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.\nOtherwise, the content returned from your `run` function will be output to stdout ( if it returned a `String`).\n\n# Showing Progress\n\nYour command's `run` function has access to an instance of [ora](https://www.npmjs.com/package/ora) available\nin `this.ui`, allowing you to display progress indicators and helpful messages while you perform other work.\nOptionally, if `this` is out of scope, you may also use the instance directly from `seeli`.\nSee the `README` in the [ora](https://www.npmjs.com/package/ora) repository for more info.\n\n```js\nconst cli = require('seeli')\n\n// Inside of Command\nthis.ui.fail('Some error message')\nthis.ui.text('Update the status bar')\nthis.ui.info('Informational text here')\n\n// Outside of Command\nfunction something() {\n  cli.ui.success('My function was called!')\n}\n```\n\n# Events\n\nInstances 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.\n\n```js\nvar EventCommand = new cli.Command({\n  args:[ '--one', '--no-two']\n, flags:{\n    one:{\n      type:Boolean\n    , event:true\n    }\n  , two:{\n      type:Boolean\n    , event:true\n    }\n  }\n, run: async function( cmd, data ){\n    return data.one \u0026\u0026 data.two\n  }\n});\n\nEventCommand.on('one', function( value ){\n  assert.equal( true, value );\n});\n\nEventCommand.on('two', function( value ){\n  assert.equal( false, value )\n});\n\nEventCommand.on('content', function( value ){\n  assert.equal( false, value );\n});\n\nEventCommand.run( null );\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesatterwhite%2Fnode-seeli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesatterwhite%2Fnode-seeli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesatterwhite%2Fnode-seeli/lists"}