npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

modular-cli-menu

v1.4.0

Published

A TypeScript library for creating modular and interactive CLI menus with plugin support and internationalization.

Readme

Modular CLI Menu

A TypeScript library for creating modular and interactive CLI menus with plugin support and internationalization.

Installation

npm install modular-cli-menu

Key Features

  • 🔌 Modular plugin system
  • 🌍 Multi-language support (i18n)
  • 🎨 Customizable menu colors
  • 🔄 Hierarchical menu navigation
  • ⌨️ Interactive input with different question types
  • 📦 Easily extensible

Basic Usage

import { print, write } from 'modular-cli-menu';

// Start the main menu
print();

// Use write for custom messages
write('Hello World', 'green');

Using print and write Methods

The library provides two main methods for output:

  • print(): Initializes and displays the menu interface
  • write(message: string, color?: ColorName): Prints custom messages with optional styling
write('Success!', 'green');
write('Error!', 'red');

Creating and Registering Plugins

To create a new plugin, define an object implementing the PluginType interface:

import { addPlugin, print } from 'modular-cli-menu';

addPlugin({
    menu: {
        name: 'test',
        parent: 'main',
        choices: [
            'menu.test.action1',
            'menu.action.back'
        ]
    },
    actions: {
        'menu.test.action1': {
            type: 'function',
            name: 'menu.test.action1',
            options: {
                message: { text: 'menu.test.action1.message', color: 'green' },
                callback: async () => {
                    // Your logic here
                }
            }
        }
    },
    languages: {
        en: {
            'menu.test.question': 'Test',
            'menu.test.action1.label': 'Action 1',
            'menu.test.action1.message': 'Something',
        },
        it: {
            'menu.test.question': 'Test',
            'menu.test.action1.label': 'Azione 1',
            'menu.test.action1.message': 'Qualcosa',
        }
    }
});

print();

In your main file, remember to register the plugin:

import myPlugin from './plugins/myPlugin';
import { addPlugin, print } from 'modular-cli-menu';

addPlugin(myPlugin);
print();

Navigation and Control Methods

The library provides several methods for menu navigation and control:

actionExit

menu.action.exit can be used to terminate the application. Add it to your menu's choices:

import { actionExit, PluginType } from 'modular-cli-menu';

const myPlugin: PluginType = {
    menu: {
        name: 'myplugin',
        choices: [
            'menu.myplugin.action1',
            'menu.action.exit' // This will add an exit option to your menu
        ]
    },
    // ... rest of the plugin configuration
};

actionGoBack

menu.action.back can be used to navigate to the previous menu. Add it to your menu's choices:

import { actionGoBack, PluginType } from 'modular-cli-menu';

const myPlugin: PluginType = {
    menu: {
        name: 'myplugin',
        choices: [
            'menu.myplugin.action1',
            'menu.action.back' // This will add a "back" option to your menu
        ]
    },
    // ... rest of the plugin configuration
};

waitForKeyPress

Use waitForKeyPress() to pause execution until the user presses a key:

import { waitForKeyPress, PluginType } from 'modular-cli-menu';

const myAction: PluginType = {
    type: 'function',
    name: 'myAction',
    options: {
        callback: async (parent) => {
            await waitForKeyPress();
            // Continue with the next actions
        },
    },
};

Action Types

The library supports different types of actions:

  1. function: Executes a callback function
  2. input: Requests user input
  3. checkbox: Allows multiple selections
  4. goto: Navigates to another menu

Function Action Example

{
    type: 'function',
    name: 'myAction',
    color: 'green',
    options: {
        message: {
            text: 'Action executed!',
            color: 'green'
        },
        callback: async () => {
            // Your logic here
        }
    }
}

Input Action Example

{
    type: 'input',
    name: 'inputAction',
    color: 'yellow',
    options: {
        message: {
            text: 'Enter a value:',
            color: 'yellow'
        },
        callback: async (value, parent) => {
            console.log(`Entered value: ${value}`);
        }
    }
}

Checkbox Action Example

{
    type: 'checkbox',
    name: 'checkboxAction',
    options: {
        message: {
            text: 'Select options:',
            color: 'cyan',
        },
        choices: ['Option 1', 'Option 2', 'Option 3'],
        callback: async (selected) => {
            console.log('Selected options:', selected);
        }
    }
}

Goto Action Example

{
    type: 'goto',
    name: 'gotoAction',
    color: 'blue',
    options: {
        callback: async (to) => {
            // Custom logic before navigating
        },
    },
}

Built-in Plugins: Retry and Language

The library includes some built-in plugins, such as retry and language, to enhance user experience and internationalization out of the box.

Retry Plugin

The retry plugin provides a menu for handling invalid input or failed actions, allowing users to easily retry an operation.

Example structure:

addActionToMenu({
    type: 'input',
    name: 'menu.foo.action',
    options: {
        message: { text: 'menu.foo.action.message'},
        callback: async (value: string) => {
            if (value.length > 0 && fs.existsSync(value)) {
                options.foo = value;
                await waitForKeyPress({ message: 'menu.foo.action.callback.success', color: 'green' });
                await print('main');
            } else {
                await print('retry', {
                    action: 'menu.foo.action', 
                    message: 'menu.foo.action.callback.error', 
                    color: 'red' 
                });
            }
        }
    }
});

You can use this plugin to prompt the user to retry an action after an error or invalid input.

Language Plugin

The language plugin allows users to switch the interface language at runtime. It updates the MENU_LANGUAGE environment variable and reloads translations.


These plugins are registered automatically, but you can customize or extend them as needed.

## Internationalization (i18n)

You can provide translations for your plugin by adding a `languages` property to your plugin object. The library will automatically merge these translations and use the correct language based on the current environment.

## Support me

[Share with me a cup of tea](https://www.buymeacoffee.com/bloris) ☕