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 🙏

© 2025 – Pkg Stats / Ryan Hefner

bob-core

v2.0.0

Published

BOB Core

Downloads

1,190

Readme

  ____   ____  ____
 | __ ) / __ \| __ )
 |  _ \| |  | |  _ \
 | |_) | |  | | |_) |
 |____/ \____/|____/

BOB Core

Your Bash Operation Buddy 💪

Build powerful TypeScript CLIs with type-safe commands and beautiful interactive prompts

npm version License: ISC TypeScript

FeaturesInstallationQuick StartDocumentationExamples


Features

Type-Safe Commands - Full TypeScript support with type inference for arguments and options
🎯 Declarative API - Define commands with simple schemas or string signatures
💬 Interactive Prompts - Built-in support for confirmations, selections, inputs, and more
🎨 Beautiful Help - Automatically generated, well-formatted help documentation
🔍 Smart Suggestions - Fuzzy matching suggests similar commands when you make typos
📦 Zero Config - Works out of the box with sensible defaults
🚀 Dual Module Support - Both CommonJS and ESM supported
⚡️ Fast & Lightweight - Minimal dependencies, maximum performance


Installation

npm install bob-core
yarn add bob-core
pnpm add bob-core

Quick Start

Create your CLI

// cli.ts
import { Cli } from 'bob-core';

const cli = new Cli({
  name: 'my-cli',
  version: '1.0.0'
});

await cli.withCommands('./commands');

const exitCode = await cli.runCommand(process.argv[2], ...process.argv.slice(3));
process.exit(exitCode);

Create a Command

Modern Schema-Based (Recommended):

// commands/greet.ts
import { Command } from 'bob-core';

export default new Command('greet', {
  description: 'Greet a user',
  arguments: {
    name: 'string'
  },
  options: {
    enthusiastic: {
      type: 'boolean',
      alias: ['e'],
      default: false,
      description: 'Add enthusiasm!'
    }
  }
}).handler((ctx, { arguments: args, options }) => {
  const greeting = `Hello, ${args.name}${options.enthusiastic ? '!' : '.'}`;
  console.log(greeting);
});

Modern Class-Based:

// commands/greet.ts
import { Command, CommandHandlerOptions, OptionsSchema } from 'bob-core';

const GreetOptions = {
  enthusiastic: {
    type: 'boolean',
    alias: ['e'],
    default: false,
    description: 'Add enthusiasm!'
  }
} satisfies OptionsSchema;
type GreetOptions = typeof GreetOptions;

const GreetArguments = {
  name: 'string'
} satisfies OptionsSchema;
type GreetArguments = typeof GreetArguments;

export default class GreetCommand extends Command<any, GreetOptions, GreetArguments> {
  constructor() {
    super('greet', {
      description: 'Greet a user',
      options: GreetOptions,
      arguments: GreetArguments
    });
  }

  handle(ctx: any, opts: CommandHandlerOptions<GreetOptions, GreetArguments>) {
    const greeting = `Hello, ${opts.arguments.name}${opts.options.enthusiastic ? '!' : '.'}`;
    console.log(greeting);
  }
}

Signature-Based:

// commands/greet.ts
import { CommandWithSignature } from 'bob-core';

export default class GreetCommand extends CommandWithSignature {
  signature = 'greet {name} {--enthusiastic|-e}';
  description = 'Greet a user';

  protected async handle() {
    const name = this.argument<string>('name');
    const enthusiastic = this.option<boolean>('enthusiastic');

    const greeting = `Hello, ${name}${enthusiastic ? '!' : '.'}`;
    console.log(greeting);
  }
}

Run It

$ node cli.js greet John
Hello, John.

$ node cli.js greet Jane --enthusiastic
Hello, Jane!

$ node cli.js help greet
Description:
  Greet a user

Usage:
  greet <name> [options]

Arguments:
  name                 (string)

Options:
  --enthusiastic, -e   Add enthusiasm! (boolean) [default: false]
  --help, -h          Display help for the given command

Interactive Prompts

Build beautiful interactive CLIs with built-in prompts:

import { Command, CommandHandlerOptions, OptionsSchema } from 'bob-core';

export default class SetupCommand extends Command<any, OptionsSchema, OptionsSchema> {
  constructor() {
    super('setup', {
      description: 'Interactive project setup'
    });
  }

  async handle(ctx: any, opts: CommandHandlerOptions<OptionsSchema, OptionsSchema>) {
    // Text input
    const name = await this.io.askForInput('Project name:');

    // Confirmation
    const useTypeScript = await this.io.askForConfirmation('Use TypeScript?', true);

    // Selection
    const framework = await this.io.askForSelect('Framework:', [
      { title: 'React', value: 'react' },
      { title: 'Vue', value: 'vue' },
      { title: 'Svelte', value: 'svelte' }
    ]);

    // Multi-select
    const features = await this.io.askForSelect(
      'Features:',
      ['ESLint', 'Prettier', 'Testing'],
      { type: 'multiselect' }
    );

    // Spinner/loader
    using loader = this.io.newLoader('Creating project...');
    await createProject({ name, framework, features });
    loader.stop();

    this.io.info('✅ Project created!');
  }
}

Context Injection

Pass shared dependencies and configuration to your commands:

interface AppContext {
  config: Config;
  database: Database;
  logger: Logger;
}

const context: AppContext = {
  config: loadConfig(),
  database: new Database(),
  logger: new Logger()
};

const cli = new Cli<AppContext>({
  ctx: context,
  name: 'my-app',
  version: '1.0.0'
});

// Access context in commands
export default new Command<AppContext>('users:list', {
  description: 'List users'
}).handler(async (ctx) => {
  const users = await ctx.database.getUsers();
  users.forEach(user => console.log(user.name));
});

Documentation

📚 Getting Started - Installation and first CLI
🔨 Creating Commands - Schema-based and signature-based approaches
⚙️ Arguments & Options - Type-safe parameters
💬 Interactive Prompts - Build interactive CLIs
🚀 Advanced Topics - Context, resolvers, error handling
Help System - Customize help output
📖 API Reference - Complete API documentation
💡 Examples - Real-world examples


Examples

Type-Safe Arguments

export default new Command('deploy', {
  arguments: {
    environment: 'string',           // Required
    region: {                        // Optional with default
      type: 'string',
      default: 'us-east-1',
      required: false
    }
  }
}).handler((ctx, { arguments: args }) => {
  // args.environment is string
  // args.region is string | null
});

Variadic Arguments

export default new Command('delete', {
  arguments: {
    files: ['string']  // Array of strings
  }
}).handler((ctx, { arguments: args }) => {
  // args.files is string[]
  args.files.forEach(file => deleteFile(file));
});

Options with Aliases

export default new Command('serve', {
  options: {
    port: {
      type: 'number',
      alias: ['p'],
      default: 3000
    },
    verbose: {
      type: 'boolean',
      alias: ['v', 'V'],
      default: false
    }
  }
});

// Usage: serve --port=8080 -v
// Usage: serve -p 8080 --verbose

Pre-Handlers for Validation

export default new Command('deploy')
  .preHandler(async (ctx) => {
    if (!ctx.isAuthenticated) {
      console.error('Not authenticated');
      return 1;  // Stop execution
    }
  })
  .handler(async (ctx) => {
    // Only runs if authenticated
  });

Command Groups

// commands/db/migrate.ts
export default new Command('db:migrate', {
  description: 'Run migrations',
  group: 'Database'
});

// commands/db/seed.ts
export default new Command('db:seed', {
  description: 'Seed database',
  group: 'Database'
});

// Displayed as:
// Database:
//   db:migrate    Run migrations
//   db:seed       Seed database

Why BOB Core?

BOB Core makes CLI development in TypeScript a breeze:

  • No Boilerplate - Define commands declaratively, not imperatively
  • Type Safety - Catch errors at compile time, not runtime
  • Great DX - Intelligent auto-complete, clear error messages
  • User Friendly - Beautiful help, smart suggestions, interactive prompts
  • Flexible - Multiple command styles, extend anything
  • Well Tested - Comprehensive test suite with Vitest

Supported Types

| Type | Description | Example | |------|-------------|---------| | 'string' | Text value | 'hello' | | 'number' | Numeric value | 42 | | 'boolean' | True/false | true | | ['string'] | String array | ['a', 'b'] | | ['number'] | Number array | [1, 2, 3] |

Note: The secret flag is not a type but a property of OptionDefinition.

Secret/Masked Input

For sensitive input like passwords, use the secret: true flag in the option definition:

options: {
  password: {
    type: 'string',      // Type is still 'string'
    secret: true,        // Flag to mask input in interactive prompts
    required: true,
    description: 'User password'
  }
}

The secret flag masks the input when prompting interactively, making it perfect for passwords and API keys.


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

ISC © Léo Hubert


⬆ back to top

Made with ❤️ by developers, for developers