A modular Ruby toolkit for building clean, correct, and robust CLI commands as plain-old Ruby classes.
- Simple - Commands are plain-old ruby classes, with options and arguments declared as attributes. All features are Ruby modules that can be included into command classes.
- Correct - CommandKit behaves like a standard UNIX command.
- Safely handles Ctrl^C / SIGINT interrupts and exits with 130.
- Safely handles broken pipes (aka
mycmd | head). - Respects common environment variables (ex:
TERM=dumbandNO_COLOR). - Uses OptionParser for POSIX option parsing.
- Disables ANSI color when output is redirected to a file or when
NO_COLORis set.
- Complete - Provides many additional CLI features.
- OS detection.
- Terminal size detection.
- ANSI coloring support.
- Interactive input.
- Rich text printing support (fields, lists, and tables).
- Subcommands (explicit or lazy-loaded) and command aliases.
- Displaying man pages for
--help/help. - Using the pager (aka
less). - XDG directories (aka
~/.config/,~/.local/share/,~/.cache/). - Exception handling / Bug reporting.
- Testable - Since commands are plain-old Ruby classes, it's easy to
initialize them and call
#mainor#run.
- No additional runtime dependencies.
- Does not implement it's own option parser.
- Not named after a comic-book Superhero.
- ruby >= 3.0.0
$ gem install command_kitgem.add_dependency'command_kit','~> 0.3'gem'command_kit','~> 0.3'require'command_kit'moduleFoomoduleCLIclassMyCmd < CommandKit::Commandusage'[OPTIONS] [-o OUTPUT] FILE'option:count,short: '-c',value: {type: Integer,default: 1},desc: "Number of times"option:output,short: '-o',value: {type: String,usage: 'FILE'},desc: "Optional output file"option:verbose,short: '-v',desc: "Increase verbose level"do@verbose += 1endargument:file,required: true,usage: 'FILE',desc: "Input file"examples['-o path/to/output.txt path/to/input.txt','-v -c 2 -o path/to/output.txt path/to/input.txt',]description'Example command'definitialize(**kwargs)super(**kwargs)@verbose=0enddefrun(file)puts"count=#{options[:count].inspect}"puts"output=#{options[:output].inspect}"puts"file=#{file.inspect}"puts"verbose=#{@verbose.inspect}"endendendend#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))require'foo/cli/my_cmd'Foo::CLI::MyCmd.startUsage: my_cmd [OPTIONS] [-o OUTPUT] FILE
Options:
-c, --count INT Number of times (Default: 1)
-o, --output FILE Optional output file
-v, --verbose Increase verbose level
-h, --help Print help information
Arguments:
FILE Input file
Examples:
my_cmd -o path/to/output.txt path/to/input.txt
my_cmd -v -c 2 -o path/to/output.txt path/to/input.txt
Example command
require'spec_helper'require'stringio'require'foo/cli/my_cmd'describeFoo::CLI::MyCmddolet(:stdin){StringIO.new}let(:stdout){StringIO.new}let(:stderr){StringIO.new}let(:env){ENV}subjectdodescribed_class.new(stdin: stdin,stdout: stdout,stderr: stderr,env: env)end# testing with raw options/argumentsdescribe"#main"docontext"when executed with no arguments"doit"must exit with -1"doexpect(subject.main([])).toeq(-1)endendcontext"when executed with -o OUTPUT"dolet(:file){ ... }let(:output){ ... }before{subject.main(["-o",output,file])}it"must create the output file"do
...
endendendend- CommandKit::Arguments
- CommandKit::BugReport
- CommandKit::Colors
- CommandKit::Command
- CommandKit::CommandName
- CommandKit::Commands
- CommandKit::Completion::Install
- CommandKit::Description
- CommandKit::Edit
- CommandKit::Env
- CommandKit::Examples
- CommandKit::ExceptionHandler
- CommandKit::FileUtils
- CommandKit::Help
- CommandKit::Interactive
- CommandKit::Main
- CommandKit::Open
- CommandKit::Options
- CommandKit::Pager
- CommandKit::Printing
- CommandKit::ProgramName
- CommandKit::Stdio
- CommandKit::Terminal
- CommandKit::Usage
- CommandKit::XDG
Special thanks to everyone who answered my questions and gave feedback on Twitter.
Copyright (c) 2021-2025 Hal Brodigan
See {file:LICENSE.txt} for details.