{"id":13403255,"url":"https://github.com/author/shell","last_synced_at":"2026-03-17T02:49:35.459Z","repository":{"id":40335011,"uuid":"233975815","full_name":"author/shell","owner":"author","description":"A very lightweight framework for building shell/CLI applications. Works in Node.js, Deno, and the browser.","archived":false,"fork":false,"pushed_at":"2022-07-18T23:05:00.000Z","size":459,"stargazers_count":79,"open_issues_count":4,"forks_count":9,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-10-08T17:07:09.941Z","etag":null,"topics":["browser","cli","cli-applications","command-line","deno","executable","javascript","javascriptnodejs","middleware","shell","web"],"latest_commit_sha":null,"homepage":"","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/author.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"coreybutler","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-01-15T01:58:06.000Z","updated_at":"2024-10-02T17:59:45.000Z","dependencies_parsed_at":"2022-08-18T02:30:24.906Z","dependency_job_id":null,"html_url":"https://github.com/author/shell","commit_stats":null,"previous_names":[],"tags_count":89,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/author%2Fshell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/author%2Fshell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/author%2Fshell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/author%2Fshell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/author","download_url":"https://codeload.github.com/author/shell/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248886316,"owners_count":21177643,"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":["browser","cli","cli-applications","command-line","deno","executable","javascript","javascriptnodejs","middleware","shell","web"],"created_at":"2024-07-30T19:01:27.424Z","updated_at":"2026-03-17T02:49:35.404Z","avatar_url":"https://github.com/author.png","language":"JavaScript","readme":"# @author.io/shell\n![Version](https://img.shields.io/github/v/tag/author/shell?label=Latest\u0026style=for-the-badge)\n\nThis is a super-lightweight framework for building text-based programs, like [CLI](https://en.wikipedia.org/wiki/Command-line_interface) applications. See the [installation guide](#installation) to jumpstart your CLI.\n\n---\nThis library is now supported by this [Chrome CLI Devtools Extension](https://chrome.google.com/webstore/detail/cli/okpglddgmnblhbdpdcmodmacgcibgfkf):\n\n![Devtools Extension](https://lh3.googleusercontent.com/WKZpJavmX4RRPyaVBFe6Vn88ZXJbjy9FCP_Mwyxo1JrWY78a9_Rh9c-sy4TawzIKy8xUmnXoxes=w640-h400-e365)\n\nYou can see the library in use (in browsers and Node.js) in this [OpenJS World 2020 talk](https://youtu.be/dw7ABwvFtdM) (The Benefits of a \"CLI First\" Development Strategy).\n\n---\n\n## Uses\n\nThere are two types of text-based apps:\n\n1. **Single Purpose** (_a_ command)\n    These are tools which may have multiple configuration options, but ultimately only do one thing. Examples include [node-tap](https://node-tap.org/), [mocha](https://mochajs.org/), [standard](https://standardjs.com/), prettier, etc.\n\n    For example, node-tap can be run on a file, using syntax like `tap [options] [\u003cfiles\u003e]`. Ultimately, this utility serves one purpose. It just runs a configured tap process.\n    \u003cbr/\u003e\n\n1. **Multipurpose** (a shell for _multiple_ commands)\n    Other tools do more than configure a script. Consider npm, which has several subcommands (like `install`, `uninstall`, `info`, etc). Subcommands often behave like their own single purpose tool, with their own unique flags, and even subcommands of their own. Docker is a good example of this, which has an entire series of management subcommands.\n\n#### Is this \"framework\" overkill?\n\n**tl;dr** Use this library to create multipurpose tools. Use [@author.io/arg](https://github.com/author/arg) to create single purpose tools.\n\n\u003cdetails\u003e\n\u003csummary\u003eDetailed Explanation\u003c/summary\u003e\n\u003cbr/\u003e\n\nThis framework was designed to support multipurpose CLI tools. At the core, it provides a clean, easily-understood, repeatable pattern for building maintainable multipurpose CLI applications.\n\nMultipurpose tools require a layer of organizational overhead to help isolate different commands and features. This overhead is unnecessary in single purpose tools. Single purpose tools just need argument parsing, which the [@author.io/arg](https://github.com/author/arg) does very well.\n\n`@author.io/arg` is embedded in this framework, making `@author.io/shell` _capable_ of creating single purpose tools, but it's merely unnecessary overhead for single purpose commands.\n\u003c/details\u003e\n\u003cbr/\u003e\n\n**Think about how your tooling evolves...**\n\nSometimes single purpose tools grow into multipurpose tools over time. Tools which start out using the `@author.io/arg` library can be transitioned into multipurpose tools using `@author.io/shell` (with reasonable ease). After all, they use the same code, just nicely separated by purpose.\n\n## Differentiating Features\n\n1. Supports **middleware** (express-style).\n1. Supports **postware** (middleware that runs after a command).\n1. **Customizable **help/usage** screens.\n1. Produces **introspectable** JSON. Load a JSON config, have a working CLI.\n1. Reusable **plugin** system.\n1. Dynamically add/remove commands.\n1. Track command execution **history**.\n1. Define **universal flags** once, reuse in all commands.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlso has better source \u0026 distribution code\u003c/b\u003e\u003c/summary\u003e\n\n1. Cross-runtime (browser, node, deno)\n1. Separation of Concerns: Arg parsing and text formatting are separate microlibs.\n1. Modern ES Module syntax\n1. 40+ unit tests\n\n\u003c/details\u003e\n\n## Basic Examples\n\nSee the [Installation Guide](#installation) when you're ready to get started.\n\nThere is a complete working example of a CLI app (with a mini tutorial) in the examples directory.\n\n_This example imports the library for Node. Simply swap the Node import for the appropriate browser import if you're building a web utility. Everything else is the same for both Node and browser environments._\n\n```javascript\nimport { Shell, Command } from '@author.io/shell'\n\n// Define a command\nconst ListCommand = new Command({\n  name: 'list',\n  description: 'List the contents of the directory.',\n  disableHelp: false, // Set to true to turn off default help messages for the entire shell (you can still provide your own). Defaults to false.\n  // arguments are listed after the command in the default help screen. Ex: \"dir list path\"\n  arguments: 'path', // Can be space/comma/tab/semicolon delimited or an array.\n  alias: 'ls',\n  // Any flag parsing options from the @author.io/arg library can be configured here.\n  // See https://github.com/author/arg#configuration-methods for a list.\n  flags: {\n    long: {\n      alias: 'l',\n      description: 'Long format'.\n      type: 'boolean',\n      default: false\n    },\n    rootDir: {\n      description: 'The root directory to list.',\n      aliases: ['input', 'in', 'src'],\n      single: true,\n      // validate: RegExp/Function (see github.com/author/arg)\n    }\n  },\n  handler (metadata, callback) {\n    // ... this is where your command actually does something ...\n\n    // Data comes from @author.io/arg lib. It looks like:\n    // {\n    //   command: \u003cCommand\u003e,\n    //   input: 'whatever user typed after \"command\"',\n    //   flags: {\n    //     recognized: {},\n    //     unrecognized: [\n    //       'whatever',\n    //       'user',\n    //       'typed'\n    //     ]\n    //   },\n    //   valid: false,\n    //   violations: [],\n    //   flag (name) { return String },\n    //   data (getter)\n    // }\n    console.log(metadata)\n\n    // A single flag's value can be retrieved with this helper method.\n    console.log(metadata.flag('long'))\n\n    // Any unrecognized flags can be retrieved by index number (0-based)\n    console.log(metadata.flag(0)) // The first unrecognized flag... returns null if it doesn't exist\n\n    // Execution callbacks are optional. If a callback is passed from the\n    // execution context to this handler, it will run after the command\n    // has finished processing\n    // (kind of like \"next\" in Express).\n    // Promises are also supported.\n    callback \u0026\u0026 callback()\n  }\n})\n\nconst shell = new Shell({\n  name: 'myapp',\n  version: '1.0.0',\n  description: 'My demo app.',\n  // This middleware runs before all command handlers.\n  use: [\n    (meta, next) =\u003e { ...; next() }\n  ],\n  // Trailers are like \"post-middleware\" that run after command handlers.\n  trailer: [\n    (meta, next) =\u003e { ...; next() }\n    (meta, next) =\u003e { console.log('All done!') }\n  ],\n  commands: [\n    // These can be instances of Command...\n    list,\n\n    // or just the configuration of a Command\n    {\n      name: 'find',\n      description: 'Search metadoc for all the things.',\n      alias: 'search',\n      flags: {\n        x: {\n          type: 'string',\n          required: true\n        }\n      },\n      handler: (data, cb) =\u003e {\n        console.log(data)\n        console.log(`Mirroring input: ${data.input}`)\n\n        cb \u0026\u0026 cb()\n      }\n      // Subcommands are supported\n      // , commands: [...]\n    }\n  ]\n})\n\n// Run a command\nshell.exec('find \"some query\"')\n\n// Run a command using a promise.\nshell.exec('find \"some query\"').then(() =\u003e console.log('Done!))\n\n// Run a command using a callback (the callback is passed to the command's handler function)\nshell.exec('find \"some query\"', () =\u003e console.log('Handled!'))\n\n// Run a command, pass a callback to the handler, and use a promise to determine when everything is done.\nshell.exec('find \"some query\"', () =\u003e console.log('Handled!')).then(() =\u003e console.log('Done!))\n\n// Output the shell's default messages\nconsole.log(shell.help)\nconsole.log(shell.usage)\nconsole.log(shell.description)\n```\n\n## Custom Handlers\n\nEach command has a handler function, which is responsible for doing something. This command receives a reference to the parsed flags.\n\n```javascript\n{\n  command: \u003cCommand\u003e,\n  input: 'Raw string of flags/arguments passed to the command',\n  flags: {\n    recognized: {},\n    unrecognized: [\n      'whatever',\n      'user',\n      'typed'\n    ]\n  },\n  flag (name) { return \u003cValue\u003e },\n  valid: false,\n  violations: []\n}\n```\n\n- **command** is the command name.\n- **input** is the string typed in after the command (flags)\n- **flags** contains the parsed flags from the [@author.io/arg](https://github.com/author/arg) library.\n- **`flag()`** is a special method for retrieving the value of any flag (recognized or unrecognized). See below.\n- **valid** indicates whether the input conforms to the parsing rules.\n- **violations** is an array of strings, where each string represents a violation of the parsing rules.\n- **data** _(getter)_ returns a key/value object with all of the known flags, as well as an _attempt_ to map any unrecognized flags with known argument names. (See basic example for argument example)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eUnderstanding flag()\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\n\nThe `flag()` method is a shortcut to help developers create more maintainable and understandable code. Consider the following example that does **not** use the flag method:\n\n```javascript\nconst cmd = new Command({\n  name: 'demo',\n  flags: {\n    a: { type: String },\n    b: { type: String }\n  },\n  handler: metadata =\u003e {\n    console.log(`A is \"${metadata.flags.recognized.a}\"`)\n    console.log(`B is \"${metadata.flags.recognized.b}\"`)\n  }\n})\n```\n\nCompare the example above to this cleaner version:\n\n```javascript\nconst cmd = new Command({\n  name: 'demo',\n  flags: {\n    a: { type: String },\n    b: { type: String }\n  },\n  handler: metadata =\u003e {\n    console.log(`A is \"${metadata.flag('a')}\"`) // \u003c-- Here\n    console.log(`B is \"${metadata.flag('b')}\"`) // \u003c-- and here\n  }\n})\n```\n\nWhile the differences aren't extreme, it abstracts the need to know whether a flag is recognized or not (or even exists). If a `flag()` is executed for a non-existant flag, it will return `null`.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eUnderstanding \u003ci\u003edata\u003c/i\u003e\u003c/b\u003e\u003c/summary\u003e\nThe `data` attribute supplied to handlers in the metadata argument contains the values for known flags, and _**attempts to map unknown arguments** to configured argument names_.\n\nFor example,\n\n```javascript\nconst shell = new Shell({\n  name: 'account',\n  commands: [{\n    name: 'create',\n    arguments: 'email displayName',\n    handler (meta) {\n      console.log(meta.data)\n    }\n  }]\n})\n\nshell.exec('create me@domain.com \"John Doe\" test1 test2')\n```\n\n_Output:_\n\n```json\n{\n  \"email\": \"me@domain.com\",\n  \"displayName\": \"John Doe\",\n  \"unknown1\": \"test1\",\n  \"unknown2\": \"test2\n}\n```\n\nIf there is a name conflict, the output will contain an array of values. For example:\n\n```javascript\nconst shell = new Shell({\n  name: 'account',\n  commands: [{\n    name: 'create',\n    arguments: 'email displayName',\n    flags: {\n      email: {\n        alias: 'e'\n      }\n    },\n    handler (meta) {\n      console.log(meta.data)\n    }\n  }]\n})\n\nshell.exec('create me@domain.com -e bob@other.com')\n```\n\n_Output:_\n\n```json\n{\n  \"email\": [\"bob@other.com\", \"me@domain.com\"],\n  \"displayName\": \"John Doe\",\n}\n```\n\n\u003e Notice the values from the known flags are _first_.\n\u003c/details\u003e\n\n## Plugins\n\nPlugins expose functions, objects, and primitives to shell handlers.\n\n_Example:_\n\nConsider an example where information is retrieved from a remote API. To do this, an HTTP request library may be necessary to make the request and parse the results. In this example, the axios library is defined as a plugin. The plugin is accessible in the metadata passed to each handler, as shown below.\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhy would you do this?\u003c/summary\u003e\n  \u003cp\u003e\n  Remember, the shell library can produce JSON (See the Introspection/Metadata Generation section). JSON is a \u003ci\u003estring\u003c/i\u003e format for storing data. The output will contain a stringified version of all the handler functions. This can be used as the configuration for another instance of a shell. In other words, you can maintain a runtime-agnostic configuration. You could use _mostly_ the same configuration for the browser, Node, Deno, Vert.x, or another JavaScript runtime. However; the modules/packages like the HTTP request module may or may not work in each runtime.\n  \u003c/p\u003e\n  \u003cp\u003e\n  Plugins allow developers to write handlers that are completely \"self contained\". It is then possible to modify the plugin configuration for each runtime without modifying every handler in the shell.\n  \u003c/p\u003e\n\u003c/details\u003e\n\u003cbr/\u003e\n\n```javascript\nimport axios from 'axios'\n\nconst sh = new Shell({\n  name: 'info',\n  plugins: {\n    httprequest: axios // replace this with any compatible library\n  },\n  commands: [{\n    name: 'person',\n    flags: {\n      name: {\n        description: 'Name of the person you want info about.',\n        required: true\n      }\n    },\n    handler (meta) {\n      meta.plugins.httprequest({\n        method: 'get',\n        url: `http://api.com/person/${meta.flag('name')}`\n      }).then(console.log).catch(console.error)\n    }\n  }, {\n    name: 'group',\n    flags: {\n      name: {\n        description: 'Name of the group you want info about.',\n        required: true\n      }\n    },\n    handler (meta) {\n      meta.plugins.httprequest({\n        method: 'get',\n        url: `http://api.com/group/${meta.flag('name')}`\n      }).then(console.log).catch(console.error)\n    }\n  }]\n})\n```\n\nCommands will inherit plugins from the shell and any parent commands. It is possible to \"override\" a plugin in any specific command.\n\n\u003cdetails\u003e\n\u003csummary\u003eOverride Example\u003c/summary\u003e\n\n```javascript\nconst sh = new Shell({\n  name: 'test',\n  plugins: {\n    test: value =\u003e {\n      return value + 1\n    }\n  },\n  commands: [{\n    name: 'cmd',\n    plugins: {\n      test: value =\u003e {\n        return value + 10\n      }\n    },\n    handler(meta) {\n      console.log(meta.test(1)) // Outputs 11\n    }\n  }]\n})\n```\n\n\u003c/details\u003e\n\n## Universal Flags\n_Common flags are automatically applied to multiple commands._\n\nSometimes a CLI app has multiple commands/subcommands that need the same flag associated with each command/subcommand. For example, if a `--note` flag were needed on every command, it would be a pain to copy/paste the config into every single command. Common flags resolve this by automatically applying to all commands from the point where the common flag is configured (i.e. the point where inheritance/nesting begins).\n\n\u003cdetails\u003e\n\u003csummary\u003eApply a common flag to ALL commands\u003c/summary\u003e\nTo include the same flag on all commands, add a common flag to the shell.\n\n```javascript\nconst shell = new Shell({\n  name: 'mycli',\n  commmonflags: {\n    note: {\n      alias: 'n',\n      description: 'Save a note about the operation.'\n    }\n  },\n  commands: [{\n    name: 'create',\n    flag: {\n      writable: {\n        alias: 'w',\n        description: 'Make it writable.'\n      }\n    },\n    ...\n  }, {\n    name: 'read',\n    ...\n  }]\n})\n\nshell.exec('create --help')\nshell.exec('read --help')\n```\n\n\u003cb\u003ecreate output:\u003c/b\u003e\n```sh\nmycli create\n\nFlags:\n  note      [-n]          Save a note about the operation.\n  writable  [-w]          Make it writable.\n```\n\n\u003cb\u003eread` output:\u003c/b\u003e\n```sh\nmycli read\n\nFlags:\n  note      [-n]          Save a note about the operation.\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eApply a common flag to a specific command/subcommands\u003c/summary\u003e\n\n```javascript\nconst shell = new Shell({\n  name: 'mycli',\n  commands: [{\n    name: 'create',\n    commmonflags: {\n      note: {\n        alias: 'n',\n        description: 'Save a note about the operation.'\n      }\n    },\n    flag: {\n      writable: {\n        alias: 'w',\n        description: 'Make it writable.'\n      }\n    },\n    commands: [...]\n    ...\n  }, {\n    name: 'read',\n    description: 'Read a directory.',\n    ...\n  }]\n})\n\nshell.exec('create --help')\nshell.exec('read --help')\n```\n\n_`create` output:_\n```sh\nmycli create\n\nFlags:\n  note      [-n]          Save a note about the operation.\n  writable  [-w]          Make it writable.\n```\n\n_`read` output:_\n```sh\nmycli read\n\n  Read a directory.\n\n```\n\u003c/details\u003e\n\n#### Filtering Universal Flags\n\nUniversal/common flags accept a special attribute named `ignore`, which will prevent the flags from being applied to specific commands. This should be used sparingly.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eCherry-picking example\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\n\n```javascript\nconst shell = new Shell({\n  name: 'mycli',\n  commmonflags: {\n    ignore: 'info', // This can also be an array of string. Fully qualified subcommands will also be respected.\n    note: {\n      alias: 'n',\n      description: 'Save a note about the operation.'\n    }\n  },\n  commands: [{\n    name: 'create',\n    handler () {}\n  }, {\n    name: 'read',\n    handler () {}\n  }, {\n    name: 'update',\n    handler () {}\n  }, {\n    name: 'delete',\n    handler () {}\n  }, {\n    name: 'info',\n    handler () {}\n  }]\n})\n```\n\nAny command, except `info`, will accepts/parse the `note` flag.\n\n\u003c/details\u003e\n\n## Middleware\n\nWhen a command is called, it's handler function is executed. Sometimes it is desirable to pre-process one or more commands. The shell middleware feature supports \"global\" middleware and \"assigned\" middleware.\n\n### Global Middleware\n\nThis middleware is applied to all handlers, unilaterally. It is useful for catching syntax errors in commands, preprocessing data, and anything else you may want to do before the actual handler is executed.\n\nFor example, the following middleware checks the input to determine if all of the appropriate flags have been set. If not, the violations are displayed and the handler is never run. If everything is correct, the `next()` method will continue processing.\n\n```javascript\nshell.use(function (metadata, next) {\n  if (!metadata.valid) {\n    metadata.violations.forEach(violation =\u003e console.log(violation))\n  } else {\n    next()\n  }\n})\n```\n\nNo matter which command the user inputs, the global middleware methods are executed.\n\n### Assigned Middleware\n\nThis middleware is assigned to one or more commands. For example:\n\n```javascript\nshell.useWith('demo', function (metadata, next) {\n  if (metadata.flag('a') === null) {\n    console.log('No \"a\" flag specified. This may slow down processing.')\n  }\n\n  next()\n})\n```\n\nThe code above would only run when the user inputs the `demo` command (or any `demo` subcommand).\n\n#### Command-Specific Assignments\n\nIt is possible to assign middleware to more than one command at a time, and it is possible to target subcommands. For example:\n\n```javascript\nshell.useWith(['demo', 'command subcommand'], function (metadata, next) {\n  if (metadata.flag('a') === null) {\n    console.log('I hope you know what you are doing!')\n  }\n\n  next()\n})\n```\n\nNotice the array as the first argument of the `useWith` method. This middleware would be assigned to `demo` command, all `demo` subcommands, the `subcommand` of `command`, and all subcommands of `subcommand`. If this sounds confusing, just know that middleware is applied to commands, including nested commands.\n\nAssigned middleware can also be applied directly to a `Command` class. For example,\n\n```javascript\nconst cmd = new Command({\n  name: 'demo',\n  flags: {\n    a: { type: String },\n    b: { type: String }\n  },\n  handler: metadata =\u003e {\n    console.log(metadata)\n  }\n})\n\ncmd.use(function (metadata, next) {\n  console.log(`this middleware is specific to the \"${cmd.name}\" command`)\n  next()\n})\n```\n\n#### Command-Exclusion Assignments\n\nSometimes middleware needs to be applied to all but a few commands. The `useExcept` method supports these needs. It is basically the opposite of `useWith`. Middleware is applied to all commands/subcommands _except_ those specified.\n\nFor example:\n\n```javascript\nconst shell = new Shell({\n  ...,\n  commands: [{\n    name: 'add',\n    handler (meta) {\n      ...\n    }\n  }, {\n    name: 'subtract',\n    handler (meta) {\n      ...\n    }\n  }, {\n    name: 'info',\n    handler (meta) {\n      ...\n    }\n  }]\n})\n\nshell.useExcept(['info], function (meta, next) {\n  console.log(`this middleware is only applied to some math commands`)\n  next()\n})\n```\n\nIn this example, the console statement would be displayed for all commands except the `info` command (and any info subcommands).\n\n### Built-in \"Middleware\"\n\nDisplaying help and version information is built-in (overridable).\n\n**Help**\n\nAppending `--help` to anything will display the help content for the shell/command/subcommand. This will respect any custom usage/help configurations that may be defined.\n\n**Shell Version**\n\nA `version` command is available on the shell. For example:\n\n```sh\n$ cmd version\n1.0.0\n```\n\nThe following common flag variations map to the version command, producing the same output:\n\n```sh\n$ cmd --version\n1.0.0\n\n$ cmd -v\n1.0.0\n```\n\nThis can be overridden by creating a command called `version`, the same way any other command is created.\n\n```javascript\nconst v = new Command({\n  name: 'version',\n  handler (meta) {\n    console.log(this.shell.version)\n  }\n})\n\nshell.add(v)\n```\n\n### Middleware Libraries\n\nOne development goal of this framework is to remain as lightweight and unopinionated as possible. Another is to be as simple to use as possible. These two goals often conflict with each other (the more features you add, the heavier it becomes). In an attempt to find a comfortable balance, some additional middleware libraries are available for those who want a little extra functionality.\n\n1. [@author.io/shell-middleware](https://github.com/author/shell-middleware)\n1. Submit a PR to add yours here.\n\n### Trailers\n_(Postware/Afterware)_\n\nTrailers operate just like middleware, but they execute _after_ the command handler is executed.\n\n```javascript\nconst shell = new Shell({\n  name: 'mycli',\n  trailer: [\n    function () { console.log('Done!' ) }\n  ],\n  command: [{\n    name: 'dir',\n    handler () {\n      console.log('ls -l')\n    },\n    // Subcommands\n    commands: [{\n      name: 'perm',\n      description: 'Permissions',\n      handler () {\n        console.log('Display permissions for a directory.')\n      }\n    }]\n  }]\n})\n\n// Execute the \"dir\" command\nshell.exec('dir')\n\n// Execute the \"perm\" subcommand\nshell.exec('dir perm')\n```\n\n_`dir` command output:_\n```sh\nls -l\nDone!\n```\n\n_`dir perm` subcommand output:_\n```sh\nDisplay permissions for a directory.\nDone!\n```\n\n### Customized Help/Usage Messages\n\n**Customizing Flag Appearance:**\n\nThe `Shell` and `Command` classes can both accept several boolean attributes to customize the description of each flag within a command. Each of these is `true` by default.\n\n1. `describeDefault`: Display the default flag value.\n1. `describeOptions`: List the valid options for a flag.\n1. `describeMultipleValues`: Appends `Can be used multiple times.` to the flag description\n1. `describeRequired`: Prepends `Required.` to the flag description whenever a flag is required.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\u003cbr/\u003e\n\n```javascript\nconst c = new Command({\n  name: '...',\n  flags: {\n    name: {\n      alias: 'nm',\n      required: true,\n      default: 'Rad Dev',\n      allowMultipleValues: true,\n      options: ['Mr Awesome', 'Mrs Awesome', 'Rad Dev'],\n      description: 'Specify a name.'\n    }\n  }\n})\n```\n\nThe help message for this flag would look like:\n\n```sh\nFlags:\n  -name       ['nm']          Required. Specify a name. Options: Mr\n                              Awesome, Mrs Awesome, Rad Dev. Can be\n                              used multiple times. (Default Rad Dev)\n```\n\u003c/details\u003e\n\u003cbr/\u003e\n\n**Customizing the Entire Message:**\n\nThis library uses a vanilla dependency (i.e. no-subdependencies) called [@author.io/table](https://github.com/author/table) to format the usage and help messages of the shell. The `Table` library can be used to create your own custom screens, though most users will likely want to stick with the defaults. If you want to customize messages, the following example can be used as a starting point. The configuration options for the table can be found in the README of its repository.\n\n```javascript\nimport { Shell, Command, Table } from '@author.io/shell'\n\nconst shell = new Shell(...)\nshell.usage = '...'\nshell.help = () =\u003e {\n  const rows = [\n    ['Command', 'Alias Names'],\n    ['...', '...']\n  ]\n\n  const table = new Table(rows)\n\n  return shell.usage + '\\n' + table.output\n}\n```\n\n**The `usage` and/or `help` attributes of an individual `Command` can also be set:**\n\n```javascript\nimport { Shell, Command, Table } from '@author.io/shell'\n\nconst cmd = new Command(...)\ncmd.usage = '...'\ncmd.help = () =\u003e {\n  const rows = [\n    ['Flags', 'Alias Names'],\n    ['...', '...']\n  ]\n\n  const table = new Table(rows)\n\n  return cmd.usage + '\\n' + table.output\n}\n```\n\nThere is also a `Formatter` class that helps combine usage/help messages internally. This class is exposed for those who want to dig into the inner workings, but it should be considered as more of an example than a supported feature. Since it is an internal class, it may change without warning (though we'll try to keep the methods consistent across releases).\n\n### Introspection/Metadata Generation\n\nA JSON metadoc can be produced from the shell:\n\n```javascript\nconsole.log(shell.data)\n```\n\nSimple CLI utilities can also be loaded entirely from a JSON file by passing the object into the shell constructor as the only argument. The limitation is no imports or hoisted variables/methods will be recognized in a shell which is loaded this way.\n\n### Autocompletion/Input Hints\n\nThis library can use a _command hinting_ feature, i.e. a shell `hint()` method to return suggestions/hints about a partial command. This feature was part of the library through the `v1.5.x` release lifecycle. In `v.1.6.0+`, this feature is no longer a part of the core library. It is now available as the [author/shell-hints plugin](https://github.com/author/shell-hints).\n\nConsider the following shell:\n\n```javascript\nimport HintPlugin from 'https://cdn.pika.dev/@author.io/browser-shell-hints'\n\nconst shell = new Shell({\n  name: 'mycli',\n  command: [{\n    name: 'dir',\n    handler () {\n      console.log('ls -l')\n    },\n    // Subcommands\n    commands: [{\n      name: 'perm',\n      description: 'Permissions',\n      handler () {\n        console.log('Display permissions for a directory.')\n      }\n    }, {\n      name: 'payload',\n      description: 'Payload',\n      handler () {\n        console.log('Display payload/footprint for a directory.')\n      }\n    }]\n  }]\n})\n\nHintPlugin.apply(shell) // \u003c-- Adds the hint method.\n\n// Help us figure out what we can do!\nconsole.log(shell.hint('dir p'))\n```\n\n_Output:_\n\n```sh\n{\n  commands: ['perm', 'payload'],\n  flags: []\n}\n```\n\nThe hint matches \"**dir p**ermission\" and \"**dir p**ayload\", but does not match any flags.\n\nIf no options/hints are available, `null` is returned.\n\nWhile this add-on provides input hints that could be used for suggestions/completions, it does **not** generate autocompletion files for shells like bash, zsh, fish, powershell, etc.\n\n\u003e There are many variations of autocompletion for different shells, which are not available in browsers (see our Devtools extension for browser completion).\n\nIf you wish to generate your own autocompletion capabilities, use the `shell.data` attribute to retrieve data for the shell (see prior section). For terminals, consider using the shell metadata with a module like [omlette](https://github.com/f/omelette) to produce autocompletion for your favorite terminal app. For browser-based CLI apps, consider using our devtools extension for an autocompletion experience.\n\n## Installation\n\n### Node.js\n\n\u003cdetails\u003e\n\u003csummary\u003eModern (ES Modules)\u003c/summary\u003e\n\u003cbr/\u003e\n\n```sh\nnpm install @author.io/shell --save\n```\n\nPlease note, you'll need a verison of Node that supports ES Modules. In Node 12, this feature is behind the `--experimental-modules` flag. It is available in Node 13+ without a flag, but the `package.json` file must have the `\"type\": \"module\"` attribute. This feature is generally available in [Node 14.0.0](https://nodejs.org) and above.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cdel\u003eLegacy (CommonJS/require)\u003c/del\u003e\u003c/summary\u003e\n\u003cbr/\u003e\n\n\u003cb\u003eDEPRECATED\u003c/b\u003e\n\n\u003cdel\u003eIf you need to use the older CommonJS format (i.e. `require`), run `npm install @author.io/shell-legacy` instead.\u003c/del\u003e\n\u003c/details\u003e\n\n### Browsers\n\n**CDN**\n\n```javascript\nimport { Shell, Command } from 'https://cdn.pika.dev/@author.io/shell'\n```\n\nAlso available from [jsdelivr](https://www.jsdelivr.com/package/npm/@author.io/shell/index.js) and [unpkg](https://unpkg.com/@author.io/shell/index.js).\n\n### Debugging\n\nEach distribution has a corresponding `-debug` version that should be installed _alongside_ the main module (the debugging is an add-on module). For example, `npm install @author.io/shell-debug --save-dev` would install the debugging code for Node. In the browser, appending the debug library adds sourcemaps.\n\n### Related Modules\n\n1. [@author.io/table](https://github.com/author/table) - Used to generate the default usage/help messages for the shell and subcommands.\n\n**Sponsors (as of 2020)**\n\n\u003ctable cellpadding=\"10\" cellspacing=\"0\" border=\"0\"\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"https://metadoc.io\"\u003e\u003cimg src=\"https://github.com/coreybutler/staticassets/raw/master/sponsors/metadoclogobig.png\" width=\"200px\"/\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ca href=\"https://butlerlogic.com\"\u003e\u003cimg src=\"https://github.com/coreybutler/staticassets/raw/master/sponsors/butlerlogic_logo.png\" width=\"200px\"/\u003e\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n","funding_links":["https://github.com/sponsors/coreybutler"],"categories":["JavaScript","cli"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fauthor%2Fshell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fauthor%2Fshell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fauthor%2Fshell/lists"}