Does your artifact manager get in the way? Join us on Oct. 8 at 10am PT, to discuss how npm can help.Sign up »


0.4.12 • Public • Published

Omelette is a simple template based autocompletion tool for Node projects with super easy API.

npm version Build Status

yarn add omelette
# or 
npm install omelette

You just have to decide your program name and CLI fragments.

omelette`github ${['pull', 'push']} ${['origin', 'upstream']} ${['master', 'develop']}`.init()

...and you are almost done! The output will look like this:

Quick Start

Implementing omelette is very easy:

import * as omelette from 'omelette';
const firstArgument = ({ reply }) => {
  reply([ 'beautiful', 'cruel', 'far' ])
const planet = ({ reply }) => {
  reply([ 'world', 'mars', 'pluto' ])
omelette`hello|hi ${firstArgument} ${planet}`.init()

Simple Event Based API ☕️

It's based on a simple CLI template.

Let's think we have a executable file with the name githubber, in a global path.

In our program, the code will be:

import * as omelette from 'omelette';
// Write your CLI template.
const completion = omelette(`githubber|gh <action> <user> <repo>`);
// Bind events for every template part.
completion.on('action', ({ reply }) => {
  reply([ 'clone', 'update', 'push' ])
completion.on('user', ({ reply }) => {
completion.on('repo', ({ before, reply }) => {
// Initialize the omelette.
// If you want to have a setup feature, you can use `omeletteInstance.setupShellInitFile()` function.
if (~process.argv.indexOf('--setup')) {
// Similarly, if you want to tear down autocompletion, use `omeletteInstance.cleanupShellInitFile()`
if (~process.argv.indexOf('--cleanup')) {
// Rest is yours
console.log("Your program's default workflow.")

complete.reply is the completion replier. You must pass the options into that method.

ES6 Template Literal API 🚀

You can use Template Literals to define your completion with a simpler (super easy) API.

import * as omelette from 'omelette';
// Just pass a template literal to use super easy API.
omelette`hello ${[ 'cruel', 'nice' ]} ${[ 'world', 'mars' ]}`.init()

Let's make the example above with ES6 TL:

import * as omelette from 'omelette'
// Write your CLI template.
  ${[ 'clone', 'update', 'push' ]}
  ${() => fs.readdirSync('/Users/')}
  ${({ before }) => [

Also you can still use lambda functions to make more complex template literals:

Advanced Template Literals

import * as omelette from 'omelette';
      ${['pull', 'push', 'star'] /* Direct command list */}
      ${require('some/other/commands') /* Import from another file */}
      ${getFromRemote('') /* Remote call at the beginning */}
      ${({ reply }) => fetch('').then(reply) /* Fetch when argument <tab>bed */}
      ${() => fs.readdirSync("/Users/") /* Access filesystem via Node */}
      ${({ before }) => [ /* Use parameters like `before`, `line`, `fragment` or `reply` */
// No extra configuration required.
console.log("Your program's default workflow.")

Async API ⏩

Omelette allows you to use async functions. You have to use onAsync and to pass Promise object to the reply function.

complete.onAsync('user', async ({ reply }) => {
  reply(new Promise((resolve) => {
    fs.readdir('/Users/', (err, users) => {

⚠️ A note about async handlers

If you are using async handlers, you have to use method to continue running your main workflow.

// ...
complete.onAsync('user', async ({ reply }) => {
  reply(new Promise((resolve) => {
    fs.readdir('/Users/', (err, users) => {
// Instead of running directly, you need to set an handler to run your main workflow.> {
  console.log("Your program's default workflow.")
// .init must be called after defining .next
// ...

Using util.promisify will make your async handlers easier.

import promisify from 'util';
complete.onAsync('user', async ({ reply }) => {
  reply(await promisify(fs.readdir)('/Users'))

Tree API 🌲

You can use simple objects as autocompletion definitions:

  cruel: ['world', 'moon'],
  beautiful: ['mars', 'pluto']


Automated Install

Installing and making your users install the autocompletion feature is very simple.

You can use simply use setupShellInitFile function.

try {
  // Pick shell init file automatically
  // Or use a manually defined init file
} catch (err) {
  // setupShellInitFile() throws if the used shell is not supported

If you use Bash, it will create a file at ~/.<program-name>/ and append a loader code to ~/.bash_profile file.

If you use Zsh, it appends a loader code to ~/.zshrc file.

If you use Fish, it appends a loader code to ~/.config/fish/ file.

TL;DR: It does the Manual Install part, basically.

Automated Uninstallation

Similarly to installation, you can use cleanupShellInitFile to undo changes done by setupShellInitFile.


As with setupShellInitFile(), wrap this in a try/catch block to handle unsupported shells.

Manual Installation

(You should add these instructions to your project's README)

In zsh, you can write these:

echo '. <(./githubber --completion)' >> .zshrc

In bash, you should write:

./githubber --completion >> ~/
echo 'source ~/' >> .bash_profile

In fish, you can write:

echo 'githubber --completion-fish | source' >> ~/.config/fish/

That's all!

Now you have an autocompletion system for your CLI tool.


There are some useful additions to omelette.


Callbacks have two parameters:

  • The fragment name (e.g.command of <command> template) (only in global event)
  • The meta data
    • fragment: The number of fragment.
    • before: The previous word.
    • line: The whole command line buffer allow you to parse and reply as you wish.
    • reply: This is the reply function to use this-less API.

Global Event

You can also listen to all fragments by "complete" event.

complete.on('complete', (fragment, { reply }) => reply(["hello", "world"]));

Numbered Arguments

You can also listen to events in order.

complete.on('$1', ({ reply }) => reply(["hello", "world"]))

Autocompletion Tree

You can create a completion tree to more complex autocompletions.

  how: {
    much: {
      is: {
        this: ['car'],
        that: ['house'],
    are: ['you'],
    many: ['cars', 'houses'],
  where: {
    are: {
      you: ['from'],
      the: ['houses', 'cars'],
    is: {
      // You can also add some logic with defining functions:
      your() {
        return ['house', 'car'];

Now, you will be able to use your completion as tree.

Thanks @jblandry for the idea.

Advanced Tree Implementations

You can seperate your autocompletion by importing objects from another file:


Short Names

You can set a short name for an executable:

In this example, githubber is long and gh is short.

omelette('githubber|gh <module> <command> <suboption>');


Now you can try it in your shell.

git clone
cd omelette/example
alias githubber="./githubber" # The app should be global, completion will search it on global level. 
./githubber --setup --debug # --setup is not provided by omelette, you should proxy it. 
# (reload bash, or source ~/.bash_profile or ~/.config/fish/ 
omelette-debug-githubber # See Debugging section 
ghb<tab> # short alias 
gh<tab> # short alias 


--debug option generates a function called omlette-debug-<programname>. (omlette-debug-githubber in this example).

When you run omlette-debug-<programname>, it will create aliases for your application. (githubber and gh in this example).

A long name:

$ githubber<tab>
clone update push

Or short name:

$ gh<tab>
clone update push

Then you can start easily.

$ ./githubber<tab>
clone update push
$ ./githubber cl<tab>
$ ./githubber clone<tab>
Guest fka
$ ./githubber clone fka<tab>
$ ./githubber clone fka<tab>


  • Windows Azure uses Omelette to support autocompletion in azure-cli.
  • Office 365 CLI uses Omelette to support autocompletion in office365-cli.
  • Visual Studio App Center CLI uses Omelette to support autocompletion in appcenter-cli.


I need your contributions to make that work better!


This project licensed under MIT.


npm i omelette

Downloadsweekly downloads









last publish


  • avatar
Report a vulnerability