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

bunosh

v0.5.0

Published

<p align="center"> <img src="assets/logo.png" alt="Logo" width="150"> </p>

Readme

Bunosh

Named after banosh, a traditional Ukrainian dish from cornmeal cooked with various ingredients


Quick Example

// Bunoshfile.js — run with: bunosh deploy
export async function deploy(env = 'production') {
  await exec`npm run build`.env({ NODE_ENV: env });
  await exec`rsync -az dist/ server:/var/www/`;
  say(`Deployed to ${env}`);
}

Installation

Single Executable (Recommended)

Download the standalone executable — no Node.js or Bun required:

macOS:

curl -fsSL https://github.com/davertmik/bunosh/releases/latest/download/bunosh-darwin-arm64.tar.gz | tar -xz
sudo mv bunosh-darwin-arm64 /usr/local/bin/bunosh

Linux:

curl -fsSL https://github.com/davertmik/bunosh/releases/latest/download/bunosh-linux-x64.tar.gz | tar -xz
sudo mv bunosh-linux-x64 /usr/local/bin/bunosh

Windows (PowerShell):

Invoke-WebRequest -Uri "https://github.com/davertmik/bunosh/releases/latest/download/bunosh-windows-x64.exe.zip" -OutFile "bunosh.zip"
Expand-Archive -Path "bunosh.zip" -DestinationPath .
Move-Item "bunosh-windows-x64.exe" "bunosh.exe"

Package Managers

# Using Bun
bun add -g bunosh

# Using npm
npm install -g bunosh

Quickstart

  1. Initialize your Bunoshfile:
bunosh init
  1. Write a command:
// Bunoshfile.js
const { exec, say } = global.bunosh;

/**
 * Builds the project for production
 */
export async function build(env = 'production') {
  await exec`npm run build`.env({ NODE_ENV: env });
  say('Build complete');
}
  1. Run it:
bunosh build
bunosh build staging

Commands

By default, Bunosh loads commands from Bunoshfile.js in the current directory.

bunosh hello

You can specify a custom file using --bunoshfile or the BUNOSHFILE environment variable:

bunosh --bunoshfile Bunoshfile.dev.js hello
BUNOSHFILE=Bunoshfile.prod.js bunosh deploy

Creating Commands

Every exported function in Bunoshfile.js becomes a CLI command:

export function hello() {
  console.log('Hello, World!');
}

export function greet(name = 'friend') {
  console.log(`Hello, ${name}!`);
}

export function deploy(env = 'staging', options = { force: false, verbose: false }) {
  if (options.verbose) console.log('Verbose mode enabled');
  console.log(`Deploying to ${env}${options.force ? ' (forced)' : ''}`);
}
bunosh hello
bunosh greet John
bunosh deploy production --force --verbose

Arguments and Options

Bunosh maps function parameters to CLI arguments automatically:

/**
 * Create a new feature branch
 * @param {string} name - Feature name (required)
 * @param {string} base - Base branch (optional, defaults to 'main')
 * @param {object} options - CLI options
 * @param {boolean} options.push - Push to remote after creation
 */
export async function feature(name, base = 'main', options = { push: false }) {
  await exec`git checkout -b feature/${name} ${base}`;

  if (options.push) {
    await exec`git push -u origin feature/${name}`;
  }
}
bunosh feature my-feature           # Creates from main
bunosh feature my-feature develop   # Creates from develop
bunosh feature my-feature --push    # Creates and pushes

Command Naming

Functions are automatically converted to kebab-case commands:

| Function Name | CLI Command | |--------------|-------------| | build | bunosh build | | gitPush | bunosh git:push | | npmInstall | bunosh npm:install | | buildAndDeploy | bunosh build:and-deploy |

Project Namespaces

Organize tasks by creating multiple Bunoshfiles. Files named Bunoshfile.<namespace>.js register commands under that namespace:

Bunoshfile.js        # bunosh build, bunosh test
Bunoshfile.dev.js    # bunosh dev:start, bunosh dev:debug
Bunoshfile.api.js    # bunosh api:deploy, bunosh api:test

Comparison

| | Bash Scripts | npm scripts | Task Runners | Bunosh | |--|--|--|--|--| | Syntax | bash/zsh | Simple commands | Custom DSL | JavaScript | | Cross-platform | No | Yes | Yes | Yes | | Ecosystem | CLI tools | npm packages | Plugin dependent | Bash + npm | | Composability | Commands | Separate scripts | Task dependencies | Import any JS code |

Tasks

Built-in tasks are available via global.bunosh:

const { exec, shell, fetch, writeToFile, copyFile, task } = global.bunosh;

Global variables are used instead of imports so bunosh works with the single-executable on any platform.

  • Async tasks: exec, shell, fetch
  • Sync tasks: writeToFile, copyFile
  • Task wrapper: task

Each task returns a TaskResult object:

const result = await shell`echo "Hello"`;
console.log(result.status);       // 'success', 'fail', or 'warning'
console.log(result.output);       // Command output
console.log(result.hasFailed);    // true if status is 'fail'
console.log(result.hasSucceeded); // true if status is 'success'

const json = await result.json(); // Structured data

task

Wraps a function into a named task with tracking and output:

const result = task('Fetch Readme file', () => {
  const content = fs.readFileSync('README.md', 'utf8');
  console.log(content);
  return content;
});

If another task runs inside a task function, its description is appended to child tasks.

exec

Runs a command using child process spawn:

await exec`npm install --verbose`;
await exec`docker build . | tee build.log`;

// With environment variables
await exec`echo $NODE_ENV`.env({ NODE_ENV: 'production' });

// In specific directory
await exec`npm install`.cwd('/tmp/project');

// Structured output
const result = await exec`git status --porcelain`;
const data = await result.json();
// Returns: { stdout: "...", stderr: "...", exitCode: 0, lines: [...] }

By default tasks print live output from stdout and stderr. To disable, use silent:

await task.silent(() => exec`npm install`);

// Or disable for all commands
task.silence();

shell

Optimized for simple commands when running under Bun:

await shell`pwd`;
await shell`ls -la`;
await shell`cat package.json`;

const result = await shell`ls -la`;
const data = await result.json();

For details see the Bun shell reference.

shell vs exec:

| Command | Best For | Implementation | Compatibility | |---------|----------|----------------|---------------| | exec | Single command execution | spawn process | Node.js + Bun, platform dependent | | shell | Cross-platform shell commands | Bun shell | Bun only, cross-platform |

fetch

Wraps the fetch API as a task:

export async function healthCheck(url) {
  const response = await fetch(url);

  if (response.ok) {
    const data = await response.json();
    say(`Service healthy: ${data.status}`);
  } else {
    yell(`Service down: ${response.status}`);
  }
}

File Operations

Template-based file writing and copying:

export function generatePage(name, description = '') {
  writeToFile('index.mdx', (line) => {
    line`name": "${name}",`;
    if (description) {
      line`description: "${description}"`;
    }
    line`---`;
  });

  copyFile('template.env', '.env');
}

Input/Output

say

Standard output:

say('Building project...');
say(`Found ${count} files to process`);

ask

User input with smart parameter detection:

const name = await ask('Project name:', 'my-app');
const proceed = await ask('Continue?', true);
const env = await ask('Select environment:', ['dev', 'staging', 'prod']);
const features = await ask('Select features:', ['TypeScript', 'ESLint', 'Tests'], { multiple: true });
const password = await ask('Enter password:', { type: 'password' });
const description = await ask('Enter description:', { editor: true });

yell

ASCII art output for important messages:

yell('BUILD COMPLETE!');

silent

Disable realtime output:

task.silence();           // Silence all task output
await shell`npm build`;
task.prints();            // Restore output

// Silence a specific task
const labels = await task.silent(() => shell(`gh api repos/:org/:repo/labels`));

Task Control

Parallel Execution

Use Promise.all() to run tasks in parallel:

const results = await Promise.all([
  exec`npm run build:frontend`,
  exec`npm run build:backend`,
  exec`npm run build:docs`
]);

Custom Tasks

Name and group operations:

await task('Build', async () => {
  await exec`npm run build:frontend`;
  await exec`npm run build:docs`;
});

Stop on Failure

By default bunosh continues execution when tasks fail. To stop immediately on failure:

export async function deployStrict() {
  task.stopOnFailures();

  await exec`npm test`;
  await exec`npm run build`;
  await exec`deploy-script`;
}

export async function cleanup() {
  task.ignoreFailures();

  await task('Remove temp files', () => shell`rm -rf tmp/*`);
  await task('Clear logs', () => shell`rm -f logs/*.log`);
  await task('Reset cache', () => shell`rm -rf .cache`);
}

Try Operations

Handle operations that might fail:

export async function checkServices() {
  const dbConnected = await task.try(() => shell`nc -z localhost 5432`);

  if (dbConnected) {
    say('Database connected');
  } else {
    say('Database unavailable, using fallback');
    await useFallbackDatabase();
  }

  const apiHealthy = await task.try(() => fetch('http://localhost:3000/health'));

  if (!apiHealthy) {
    yell('API IS DOWN!');
  }
}

Documentation

License

MIT License - see LICENSE file for details.


Made in Ukraine