Smol - Josh Brody Smol | Josh Brody
All projects
Ruby Gem

Smol

Zero-dependency CLI and REPL framework for Ruby.

Smol is a lightweight framework for building command-line applications in Ruby. It handles both single-command execution and interactive shell modes. No dependencies beyond the standard library.

Why it exists

Building CLI tools in Ruby usually goes one of two ways. Either you reach for Thor or Optparse and end up with a lot of boilerplate for simple things, or you roll your own and spend more time on argument parsing than actual functionality.

Or, like me, you just build your own.

I wanted something smaller. Define commands with a DSL, get a REPL for free, add health checks without ceremony. The framework should stay out of the way until you need it.

How it works

An App holds commands, configuration, and mounted sub-apps. Commands are classes that define a single action with arguments and options. Checks are simple pass/fail health checks you can run from the CLI or REPL.

Commands auto-register to their parent app by walking the Ruby module hierarchy. No manual wiring required unless you want it.

Dual-mode execution

Every Smol app can run two ways:

# Single command
myapp deploy --env=production

# Interactive shell
myapp
> deploy --env=production
> help
> exit

The REPL includes readline support, command history saved across sessions, and tab completion. Both modes can be enabled or disabled independently.

Auto-registration

This is the part I’m most pleased with. Define a command inside your app’s namespace and it just works:

class MyApp::Commands::Deploy < Smol::Command
  desc "Deploy the application"
  option :env, type: :string, default: "staging"

  def run
    output.success "Deploying to #{options[:env]}"
  end
end

Smol finds the parent app by examining class names. No register calls, no inheritance chains to trace. You can still wire things manually if you prefer explicit over magic.

Health checks

Same pattern as commands:

class MyApp::Checks::Database < Smol::Check
  title "Database connection"

  def run
    ActiveRecord::Base.connection.execute("SELECT 1")
    pass "Connected"
  rescue => e
    fail "Cannot connect: #{e.message}"
  end
end

Run myapp check to see them all. Useful for deploy verification or debugging production issues.

Configuration

Settings defined once, read from environment variables automatically:

class MyApp < Smol::App
  setting :api_key, type: :string
  setting :timeout, type: :integer, default: 30
  setting :verbose, type: :boolean, default: false
end

API_KEY in your environment becomes config.api_key in your code. The REPL has a built-in config command to inspect current values.

Sub-apps

Mount apps inside other apps for nested command structures:

class AdminApp < Smol::App
  banner "Admin commands"
end

class MyApp < Smol::App
  mount AdminApp, as: :admin
end

In the REPL, type admin to enter the sub-app’s context. Or run myapp admin:users:list directly from the shell.

The output helpers

Colored output, tables, and interactive prompts are included but not required.

Stack

Ruby

Stay in the loop

Occasional essays on design, tools, and the craft of building things. No spam, unsubscribe anytime.

Ambient weather

The background of this site reflects the current weather and time of day in Saint Paul. The orbs shift in color and behavior based on what's happening outside my window.

Learn more about how this works