{"id":13876132,"url":"https://github.com/hongaar/bandersnatch","last_synced_at":"2025-03-16T13:31:47.496Z","repository":{"id":37470879,"uuid":"224054835","full_name":"hongaar/bandersnatch","owner":"hongaar","description":"➰ Simple and intuitive yet powerful and versatile framework for Node.js CLI programs","archived":false,"fork":false,"pushed_at":"2024-04-10T23:57:16.000Z","size":7972,"stargazers_count":47,"open_issues_count":5,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-04-11T02:12:16.569Z","etag":null,"topics":["cli","enquirer","nodejs","parser","repl","typescript","yargs"],"latest_commit_sha":null,"homepage":"https://github.com/hongaar/bandersnatch/blob/main/README.md","language":"TypeScript","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/hongaar.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}},"created_at":"2019-11-25T22:37:59.000Z","updated_at":"2024-04-15T15:17:41.854Z","dependencies_parsed_at":"2023-02-18T18:15:48.560Z","dependency_job_id":"85b863d2-fd50-4678-93a7-a4cf4fa10818","html_url":"https://github.com/hongaar/bandersnatch","commit_stats":{"total_commits":560,"total_committers":10,"mean_commits":56.0,"dds":0.4107142857142857,"last_synced_commit":"6628bf9c8088d052ea721c38adda150af5072170"},"previous_names":[],"tags_count":74,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hongaar%2Fbandersnatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hongaar%2Fbandersnatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hongaar%2Fbandersnatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hongaar%2Fbandersnatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hongaar","download_url":"https://codeload.github.com/hongaar/bandersnatch/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243818171,"owners_count":20352629,"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":["cli","enquirer","nodejs","parser","repl","typescript","yargs"],"created_at":"2024-08-06T06:01:03.056Z","updated_at":"2025-03-16T13:31:46.607Z","avatar_url":"https://github.com/hongaar.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","cli"],"sub_categories":[],"readme":"# bandersnatch [![npm](https://img.shields.io/npm/v/bandersnatch)](https://www.npmjs.com/package/bandersnatch)\n\n```ts\nprogram()\n  .default(\n    command()\n      .description(\"What is bandersnatch?\")\n      .action(() =\u003e console.log(description)),\n  )\n  .run();\n```\n\n```\n$ ./bandersnatch\nSimple and intuitive yet powerful and versatile framework for Node.js CLI programs\n```\n\n## Features\n\n- 🌊 [Fluid](https://www.martinfowler.com/bliki/FluentInterface.html) syntax,\n  intuitive to use\n- 🔜 Autocompletion of commands, arguments, options and choices\n- ➰ Built-in [REPL](https://en.wikipedia.org/wiki/Read–eval–print_loop) for\n  interactive programs with 🔙 Command history\n- 💬 Can prompt for missing arguments\n- 🤯 Built for TypeScript, command arguments are fully typed\n- 🪆 Supports singular (e.g. `foo`) and nested commands (e.g. `foo bar`)\n- 🏃 Argument types are guaranteed at runtime (coming soon)\n\n## Table of contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Getting started](#getting-started)\n  - [Installation](#installation)\n  - [Simple example](#simple-example)\n  - [Error handling](#error-handling)\n  - [REPL](#repl)\n  - [Prompt](#prompt)\n  - [TypeScript](#typescript)\n- [API](#api)\n  - [`program(options)`](#programoptions)\n    - [`program.description(description)`](#programdescriptiondescription)\n    - [`program.prompt(prompt)`](#programpromptprompt)\n    - [`program.add(command)`](#programaddcommand)\n    - [`program.default(command)`](#programdefaultcommand)\n    - [`program.run(command)`](#programruncommand)\n    - [`program.repl()`](#programrepl)\n    - [`program.runOrRepl()`](#programrunorrepl)\n    - [`program.isRepl()`](#programisrepl)\n    - [`program.on(event, listener)`](#programonevent-listener)\n  - [`command(name, options)`](#commandname-options)\n    - [`command.description(description)`](#commanddescriptiondescription)\n    - [`command.hidden()`](#commandhidden)\n    - [`command.argument(name, options)`](#commandargumentname-options)\n    - [`command.option(name, options)`](#commandoptionname-options)\n    - [`command.add(command)`](#commandaddcommand)\n    - [`command.default()`](#commanddefault)\n    - [`command.action(function)`](#commandactionfunction)\n- [Design principles](#design-principles)\n  - [Errors](#errors)\n  - [Output](#output)\n- [Bundle](#bundle)\n- [Todo](#todo)\n- [Contributing](#contributing)\n  - [Local install](#local-install)\n  - [Devcontainer](#devcontainer)\n- [Credits](#credits)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Getting started\n\n### Installation\n\n```bash\n# Add dependency\nyarn|npm add bandersnatch\n```\n\n\u003e [!NOTE]  \n\u003e We recommend using an _Active LTS_ or _Maintenance LTS_ Node.js\n\u003e [version](https://nodejs.org/en/about/releases/). _Current_ versions are\n\u003e tested, but not guaranteed to work.\n\n### Simple example\n\nLet's create a simple program `foo.js`:\n\n```js\nimport { program, command } from \"bandersnatch\";\n\nconst cmd = command()\n  .default()\n  .description('Outputs \"bar\".')\n  .action(() =\u003e console.log(\"bar\"));\n\nconst app = program().description(\"foo\").add(cmd);\n\napp.run();\n```\n\nThis creates a new program, adds a default command which logs \"bar\" to the\nstdout, and runs the program.\n\nNow try your program by running it:\n\n```\n$ node foo.js\nbar\n```\n\nTry running `node foo.js --help` to see the auto-generated help output:\n\n```\n$ node foo.js help\nfoo.js\n\nOutputs \"bar\".\n\nCommands:\n  foo.js     Outputs \"bar\".                                            [default]\n\nOptions:\n  --help     Show help                                                 [boolean]\n  --version  Show version number                                       [boolean]\n```\n\n### Error handling\n\nWe first create a new program called `cat.js` which is a naive version of the\n`cat` program we all know:\n\n```js\nimport { readFileSync } from \"fs\";\nimport { program, command } from \"bandersnatch\";\n\nconst cat = command(\"cat\")\n  .description(\"Concatenate files\")\n  .argument(\"files\", { variadic: true })\n  .action(({ files }) =\u003e\n    console.log(\n      files.reduce((str, file) =\u003e str + readFileSync(file, \"utf8\"), \"\"),\n    ),\n  );\n\nprogram().default(cat).run();\n```\n\nNow try your program by running it:\n\n```\n$ node cat.js somefile\ncontents of somefile\n```\n\nHowever, when `somefile` doesn't exist, we get are faced with an ugly unhandled\npromise rejection warning/error (depending on the Node.js version you're using).\n\nLet's fix that:\n\n```diff\n-program().default(cat).run()\n+program()\n+  .default(cat)\n+  .run()\n+  .catch((err) =\u003e {\n+    console.error(`There was a problem running this command:\\n${String(err)}`)\n+    process.exit(1)\n+  })\n```\n\nWhich will yield:\n\n```\n$ node cat.js somefile\nThere was a problem running this command:\nError: ENOENT: no such file or directory, open 'somefile'\n```\n\n### REPL\n\nA program can also show an interactive\n[REPL](https://en.wikipedia.org/wiki/Read–eval–print_loop) to make interacting\nwith more complex programs easier and to enable autocompleting of commands and\narguments.\n\nLet's create a new program `dice.js` with a command to roll a dice:\n\n```js\nimport { program, command } from \"bandersnatch\";\n\nasync function rng(bounds) {\n  const [min, max] = bounds;\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nconst dice = program().add(\n  command(\"roll\")\n    .option(\"min\", { default: 1 })\n    .option(\"max\", { default: 6 })\n    .action(async (args) =\u003e {\n      console.log(await rng([args.min, args.max]));\n    }),\n);\n\ndice.repl();\n```\n\nThis code defines a program `dice` and a command `roll` with two options, both\nof which will inherit a default value. When the command is executed, it calls an\nasync random number generator (async only for illustrative purposes) and writes\nits results to stdout.\n\nThe last line in our code runs the program as a interactive REPL, which means it\nwon't accept any arguments on the command line, but render a prompt instead.\nThis prompt will read any user input, parse it, and execute matching commands.\n\nTry rolling the dice:\n\n```\n$ node dice.js\n\u003e roll\n5\n```\n\nThe REPL can autocomplete commands, arguments, options and choices. Try typing\nonly the letter `r` and then hit _TAB_. This works for options as well:\n\n```\n$ node dice.js\n\u003e r\n[TAB]\n\u003e roll -\n[TAB]\n\u003e roll --m\n[TAB] [TAB]\n--min   --max\n```\n\n### Prompt\n\nBandersnatch can also ask a user for input if arguments were not provided on the\ncommand line:\n\nLet's say we want to write a program `pizza.js` which takes pizza orders:\n\n```js\nimport { program, command } from \"bandersnatch\";\n\nconst cmd = command()\n  .argument(\"address\", {\n    prompt: \"Your address\",\n  })\n  .option(\"name\", {\n    description: \"Your name\",\n    default: \"anonymous\",\n    prompt: true,\n  })\n  .option(\"size\", {\n    description: \"Choose pizza size\",\n    choices: [\"small\", \"medium\", \"large\"],\n    default: \"medium\",\n    prompt: true,\n  })\n  .option(\"toppings\", {\n    description: \"Pick some toppings\",\n    choices: [\"mozzarella\", \"pepperoni\", \"veggies\"],\n    default: [\"mozzarella\"],\n    prompt: true,\n  })\n  .option(\"confirmed\", {\n    description: \"Order pizza?\",\n    default: true,\n    prompt: true,\n  })\n  .action((args) =\u003e {\n    console.log(args);\n  });\n\nprogram().description(\"Order a pizza\").default(cmd).run();\n```\n\nAnd run it:\n\n```\n$ node pizza.js\n? Your address The Netherlands\n? Your name Joram\n? Choose pizza size small\n? Pick some toppings veggies\n? Order pizza? Yes\n{\n  name: 'Joram',\n  size: 'small',\n  toppings: [ 'veggies' ],\n  confirmed: true,\n  address: 'The Netherlands'\n}\n```\n\nYou can choose to specify parameters on the command line, in which case you\nwon't get a prompt for these options:\n\n```\n$ node pizza.js \"The Netherlands\" --name Joram --confirmed\n? Choose pizza size small\n? Pick some toppings veggies\n? Order pizza? Yes\n{\n  name: 'Joram',\n  size: 'small',\n  toppings: [ 'veggies' ],\n  confirmed: true,\n  address: 'The Netherlands'\n}\n```\n\n\u003e [!WARNING]  \n\u003e Please note that even though `--confirmed` was specified on the command line,\n\u003e it was still being prompted. This is a known issue. In this case, the default\n\u003e value was the same as the input, in which case bandersnatch doesn't know\n\u003e whether a value was explicitly passed in or inherited from the default value.\n\n### TypeScript\n\nBandersnatch works perfectly well with non-TypeScript codebases. However, when\nyou do use TypeScript the command arguments are fully typed.\n\nLet's rename the example program to `pizza.ts` and add some minor type hints to\nillustrate this:\n\n```diff\n   .option('size', {\n     description: 'Choose pizza size',\n-    choices: ['small', 'medium', 'large'],\n+    choices: ['small', 'medium', 'large'] as const,\n     default: 'medium',\n     prompt: true,\n   })\n   .option('toppings', {\n     description: 'Pick some toppings',\n-    choices: ['mozzarella', 'pepperoni', 'veggies'],\n+    choices: ['mozzarella', 'pepperoni', 'veggies'] as const,\n     default: ['mozzarella'],\n     prompt: true,\n   })\n```\n\nThe first argument passed to the action handler function is now typed like this:\n\n```ts\ntype Args = {\n  address: string;\n  name: string;\n  size: \"small\" | \"medium\" | \"large\";\n  toppings: (\"mozzarella\" | \"pepperoni\" | \"veggies\")[];\n  confirmed: boolean;\n};\n```\n\n\u003e [!TIP]  \n\u003e More examples in the\n\u003e [examples](https://github.com/hongaar/bandersnatch/tree/main/examples)\n\u003e directory.\n\n## API\n\nAll methods are chainable unless the docs mention otherwise.\n\n### `program(options)`\n\nCreates a new program. Options (object, optional) can contain these keys:\n\n- `description` (string, optional) is used in help output.\n- `prompt` (string, default: `\u003e `) use this prompt prefix when in REPL mode.\n- `help` (boolean, default: true) adds `help` and `--help` to the program which\n  displays program usage information.\n- `version` (boolean, default: true) adds `version` and `--version` to the\n  program which displays program version from package.json.\n- `historyFile` (string | null, default: {homedir}/.bandersnatch_history) is a\n  path to the app history file. Set to NULL to disable.\n- `exit` (boolean | () =\u003e void, default: () =\u003e process.exit()) Specifies whether\n  to add a default behaviour for an `exit` command. `false` disables the default\n  implementation, a custom function will be installed as the actual handler.\n- `parserConfiguration` (object, optional) can be used to modify the parser\n  configuration. For available options, see\n  - https://github.com/yargs/yargs/blob/main/docs/api.md#parserConfiguration.\n\n#### `program.description(description)`\n\nSets the program description (string, required) used in help output.\n\n#### `program.prompt(prompt)`\n\nUse this prompt prefix (string, required) when in REPL mode.\n\n#### `program.add(command)`\n\nAdds a command to the program.\n\n```js\nprogram().add(command(...))\n```\n\n#### `program.default(command)`\n\nAdds a default command to the program. Shorthand for:\n\n```js\nprogram().add(command(...).default())\n```\n\n#### `program.run(command)`\n\nUses process.argv or passed in command (string, optional) to match and execute\ncommand. Returns promise.\n\n```js\nprogram()\n  .add(command(...))\n  .run()\n```\n\n#### `program.repl()`\n\nStart a read-eval-print loop. Returns promise-like REPL instance.\n\n```js\nprogram()\n  .add(command(...))\n  .repl()\n```\n\n#### `program.runOrRepl()`\n\nInvokes `run()` if process.argv is set, `repl()` otherwise. Returns promise or\npromise-like REPL instance.\n\n```js\nprogram()\n  .add(command(...))\n  .runOrRepl()\n```\n\n#### `program.isRepl()`\n\nReturns `true` if program is running a REPL loop, `false` otherwise.\n\n#### `program.on(event, listener)`\n\nAttaches a listener function for the event. Currently, these events are\nsupported:\n\n```js\n// Fired before a command action is invoked\nprogram().on(\"run\", (cmd) =\u003e logger.debug(`Running ${cmd}`));\n```\n\n### `command(name, options)`\n\nCreates a new command.\n\n- Name (string, optional) is used to invoke a command. When not used as the\n  default command, a name is required.\n- Options (object, optional) can contain these keys:\n  - `description` (string) is used in help output.\n  - `hidden` (boolean) hide command from help output and autocomplete.\n\n#### `command.description(description)`\n\nSets the command description (string, required) used in help output.\n\n#### `command.hidden()`\n\nHide command from help output and autocomplete.\n\n#### `command.argument(name, options)`\n\nAdds a positional argument to the command.\n\n- Name (string, required) is used to identify the argument.\n- Options can be provided to change the behavior of the argument. Object with\n  any of these keys:\n  - `description` (string) is used in help output.\n  - `optional` (boolean) makes this argument optional.\n  - `variadic` (boolean) eagerly take all remaining arguments and parse as an\n    array. Only valid for the last argument.\n  - `type` (string) one of `\"boolean\"|\"number\"|\"string\"` which determines the\n    runtime type of the argument.\n  - `default` (any) default value for the argument.\n  - `choices` (array) any input value should be included in the array, or it\n    will be rejected.\n  - `prompt` (boolean|string) prompts for missing arguments. If it is true, it\n    will use the arguments description or name as the question text. If it is a\n    string, it will be used as the question text.\n  - `alias` (string|array) alias or aliases for the argument.\n  - `coerce` (function) transform function for this argument value (untyped).\n  - `requires` (string|array) make another option or argument required if the\n    argument is present\n  - `excludes` (string|array) exclude another options or argument if the\n    argument is present\n\n#### `command.option(name, options)`\n\nAdds an option to the command.\n\n- Name (string, required) is used to identify the option.\n- Options (object, optional) can be provided to change the behavior of the\n  option. Object with any of these keys:\n  - `description` (string) is used in help output.\n  - `type` (string) one of `\"array\"|\"boolean\"|\"count\"|\"number\"|\"string\"` which\n    determines the runtime type of the argument. Use count for the number of\n    times an option was provided (e.g. verbosity levels).\n  - `default` (any) default value for the argument.\n  - `choices` (array) any input value should be included in the array, or it\n    will be rejected.\n  - `prompt` (boolean|string) prompts for missing arguments. If it is true, it\n    will use the arguments description or name as the question text. If it is a\n    string, it will be used as the question text.\n  - `alias` (string|array) alias or aliases for the option.\n  - `coerce` (function) transform function for this option value (untyped).\n  - `required` (boolean) makes the option required.\n  - `requires` (string|array) make another option or argument required if the\n    option is present\n  - `excludes` (string|array) exclude another options or argument if the option\n    is present\n\n#### `command.add(command)`\n\nAdds a sub-command to the command.\n\n#### `command.default()`\n\nMark command as default. Default commands are executed immediately and don't\nrequire a name.\n\n#### `command.action(function)`\n\nFunction which executes when the command is invoked. Is called with these\narguments:\n\n1. Args (object) is an object containing key/value pairs of parsed arguments and\n   options.\n2. Command runner (function) can be invoked with one (string) parameter to\n   execute another command.\n\n## Design principles\n\nIn general, bandersnatch is designed to create\n[twelve-factor apps](https://12factor.net/).\n\n### Errors\n\nThe bandersnatch API allows to catch errors in a promise-like way. The `run` and\n`repl` program methods return either a promise or promise-like object which can\nbe used to handle program errors:\n\n```js\nprogram()\n  .default(\n    command()\n      .description(\"This command will always fail\")\n      .action(function () {\n        throw new Error(\"Whoops\");\n      }),\n  )\n  .runOrRepl()\n  .catch((error) =\u003e {\n    console.error(\"[failed]\", String(error));\n\n    if (!app.isRepl()) {\n      process.exit(1);\n    }\n  });\n```\n\n### Output\n\nPrograms are encouraged to use the following conventions with regards to output,\nbased on the\n[POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/functions/stdin.html).\n\n- When a program is designed to be used in a scripting environment and its\n  output should be available as stdin for other programs, use stdout for\n  printing output and stderr for diagnostic output (e.g. progress and/or error\n  messages).\n- When a program is designed to be used as a service (twelve-factor app), use\n  stdout/stderr as a logging mechanism for informative messages/error and\n  diagnostic messages.\n\nBandersnatch has no built-in method for writing to stdout/stderr. Node.js\nprovides [everything you need](https://nodejs.org/api/console.html).\n\n## Bundle\n\nThere are many options to bundle your application for distribution. We'll\ndiscuss a common pattern.\n\n\u003e [!TIP]  \n\u003e An example can be found in the\n\u003e [examples/bundle](https://github.com/hongaar/bandersnatch/tree/main/examples/bundle)\n\u003e directory.\n\nInit a `package.json` if needed:\n\n```\nmkdir echo \u0026\u0026 cd echo\nyarn init\n```\n\nInstall dependencies:\n\n```\nyarn add bandersnatch\nyarn add typescript @types/node pkg --dev\n```\n\nAnd create an example app in `src/cli.ts`:\n\n```ts\nimport { program, command } from \"bandersnatch\";\n\nexport default program().default(\n  command(\"echo\")\n    .description(\"Echo something in the terminal\")\n    .argument(\"words\", { description: \"Say some kind words\", variadic: true })\n    .action(console.log),\n);\n```\n\nBuilding your app with TypeScript is very powerful, but runtime compilation is\nslow so we compile the code ahead of time.\n\nAdd a `tsconfig.json`, similar to:\n\n```json\n{\n  \"include\": [\"./src\"],\n  \"compilerOptions\": {\n    \"target\": \"es2017\",\n    \"module\": \"commonjs\",\n    \"lib\": [\"es2017\"],\n    \"declaration\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"strict\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true,\n    \"moduleResolution\": \"node\"\n  }\n}\n```\n\nAdd these scripts to your `package.json`:\n\n```diff\n {\n   \"name\": \"echo\",\n   \"version\": \"1.0.0\",\n   \"main\": \"index.js\",\n   \"license\": \"MIT\",\n+  \"scripts\": {\n+    \"prepublishOnly\": \"yarn build\",\n+    \"build\": \"tsc\",\n+  },\n   \"dependencies\": {\n     \"bandersnatch\": \"^1.0.0\"\n   },\n   \"devDependencies\": {\n     \"pkg\": \"^5.3.1\",\n     \"typescript\": \"^4.4.2\"\n   }\n }\n```\n\nAnd compile now by running `yarn build`.\n\nNext, we need to create a simple entry point `echo.js`, which can be run with\nnode:\n\n```bash\n#!/usr/bin/env node\n\nrequire('./dist/cli').default.run()\n```\n\nTo run your app, users may want to run `yarn global add echo`. For this to work,\nwe need to make a small adjustment to `package.json`:\n\n```diff\n {\n   \"name\": \"echo\",\n   \"version\": \"1.0.0\",\n-  \"main\": \"index.js\",\n+  \"bin\": \"echo.js\",\n+  \"files\": [\n+    \"dist\"\n+  ],\n   \"license\": \"MIT\",\n   \"scripts\": {\n     \"prepublishOnly\": \"yarn build\",\n     \"build\": \"tsc\",\n   },\n   \"dependencies\": {\n     \"bandersnatch\": \"^1.0.0\"\n   },\n   \"devDependencies\": {\n     \"pkg\": \"^5.3.1\",\n     \"typescript\": \"^4.4.2\"\n   }\n }\n```\n\nYou can now `npm publish`.\n\nTo create a binary (your app with Node.js bundled), add this script to\n`package.json`:\n\n```diff\n {\n   \"name\": \"echo\",\n   \"version\": \"1.0.0\",\n   \"bin\": \"echo.js\",\n   \"files\": [\n     \"dist\"\n   ],\n   \"license\": \"MIT\",\n   \"scripts\": {\n     \"prepublishOnly\": \"yarn build\",\n     \"build\": \"tsc\",\n+    \"bundle\": \"yarn build \u0026\u0026 pkg -t host .\"\n   },\n   \"dependencies\": {\n     \"bandersnatch\": \"^1.0.0\"\n   },\n   \"devDependencies\": {\n     \"pkg\": \"^5.3.1\",\n     \"typescript\": \"^4.4.2\"\n   }\n }\n```\n\n\u003e [!TIP]  \n\u003e Omit `-t host` to create binaries for all platforms.\n\nRun `yarn bundle` and then `./echo --help`. 💪\n\nOptionally deploy to GitHub, S3, etc. using your preferred CD method if needed.\n\n## Todo\n\nSee [TODO.md](TODO.md)\n\n## Contributing\n\nContributions are very welcome. Please use\n[conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).\n\n### Local install\n\n```bash\n# Clone and install\ngit clone git@github.com:hongaar/bandersnatch.git\ncd bandersnatch\nyarn install\nyarn husky install\n\n# Run an example\nyarn start examples/foo.ts\n```\n\n### Devcontainer\n\nA devcontainer configuration is included in this repo to\n[get started quickly](https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container).\n\n## Credits\n\n©️ Copyright 2022 [Joram van den Boezem](https://joram.dev)  \n♻️ Licensed under the [MIT license](LICENSE)  \n💡 Inspired by [vorpal](https://vorpal.js.org/)  \n⚡ Powered by [yargs](http://yargs.js.org) and\n[enquirer](https://github.com/enquirer/enquirer)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhongaar%2Fbandersnatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhongaar%2Fbandersnatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhongaar%2Fbandersnatch/lists"}