Command-line tools might not have fancy interfaces, but they’re some of the most powerful and time-saving tools a developer can build. From simple automation scripts to complex developer utilities, CLIs (Command Line Interfaces) are at the heart of how modern developers work.
In this post, we’ll walk through how to build a robust CLI with Node.js, and we’ll take a close look at SamerArtisan — a Laravel Artisan–inspired framework that brings structure, elegance, and TypeScript support to CLI development.
Why Build a CLI Tool?
Think about how often you type git
, npm
, or docker
in your terminal. C...
Command-line tools might not have fancy interfaces, but they’re some of the most powerful and time-saving tools a developer can build. From simple automation scripts to complex developer utilities, CLIs (Command Line Interfaces) are at the heart of how modern developers work.
In this post, we’ll walk through how to build a robust CLI with Node.js, and we’ll take a close look at SamerArtisan — a Laravel Artisan–inspired framework that brings structure, elegance, and TypeScript support to CLI development.
Why Build a CLI Tool?
Think about how often you type git
, npm
, or docker
in your terminal. CLI tools let us control complex systems quickly, script them easily, and automate repetitive tasks.
Here’s why developers love CLIs:
- ⚙️ Automation-friendly – Easily scriptable and perfect for CI/CD.
- 💡 Lightweight – No need for a GUI, ideal for servers and terminals.
- 🧩 Composable – Can be chained with other tools using pipes and redirects.
- ⚡ Fast – Interacts directly with the system, no browser overhead.
In short: CLIs make developers faster.
The Node.js CLI Ecosystem
Node.js has become a favorite for building CLI tools, thanks to its simplicity and cross-platform nature. The most common library for this is Commander.js, which handles command parsing, flags, and options well.
But as your tool grows, Commander.js can start to feel repetitive — lots of boilerplate, little structure. That’s where SamerArtisan comes in.
Inspired by Laravel’s “Artisan” console, SamerArtisan gives Node.js CLIs a framework-like experience — complete with classes, structure, interactivity, and first-class TypeScript support.
Commander.js vs SamerArtisan — Which Should You Use?
🧱 Commander.js: The Classic Choice
Commander.js is perfect when you need:
- A simple CLI with a few commands.
- A quick prototype or internal tool.
- Minimal dependencies and maximum control.
Example:
const { Command } = require('commander');
const program = new Command();
program
.name(‘my-cli’)
.description(‘CLI for simple greetings’)
.version(‘0.8.0’);
program
.command(‘greet’)
.description(‘Greet someone’)
.argument(‘<name>’, ‘person to greet’)
.option(‘-u, –uppercase’, ‘uppercase the greeting’)
.action((name, options) => {
const greeting = Hello </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2">!
;
console.log(options.uppercase ? greeting.toUpperCase() : greeting);
});
program.parse();
Straightforward — but not very scalable.
💎 SamerArtisan: Structure with Style
SamerArtisan shines when your CLI needs more than a few commands. It gives you:
- Organized command classes and inheritance.
- Interactive prompts, progress bars, and tables.
- TypeScript support for safety and autocompletion.
- A Laravel-like experience for developers.
- A scalable foundation for large tools.
Example:
const { SamerArtisan, Command } = require("samer-artisan");
class GreetCommand extends Command {
signature = “greet {name} {–uppercase}”;
description = “Greet someone”;
handle() {
const name = this.argument(‘name’);
const uppercase = this.option(‘uppercase’);
const greeting = Hello </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2">!
;
this.info(uppercase ? greeting.toUpperCase() : greeting);
}
}
SamerArtisan.add(new GreetCommand());
SamerArtisan.parse();
Cleaner, more readable, and ready to scale.
Getting Started with SamerArtisan
Let’s build a simple project management CLI that initializes projects and manages tasks.
Step 1: Install
npm install samer-artisan
Step 2: Set Up Your CLI Entry File
// cli.js
const { SamerArtisan } = require("samer-artisan");
SamerArtisan
.projectName(“ProjectManager”)
.root(__dirname)
.load(“commands”)
.parse();
Step 3: Create a Command
You can generate a command class automatically:
node cli.js make:command InitProject
This gives you a clean template:
// commands/InitProject.js
const { Command } = require("samer-artisan");
class InitProject extends Command {
/**
- The name and signature of the console command.
*/
signature =
project:init { a: First arg } { b } { c*: Third arg } { --dog : First opt }
;
/**
- The console command description.
*/
description = “command description”;
/**
- Execute the command
*/
handle() {
this.info(“InitProject command works!”);
}
}
module.exports = InitProject;
Features That Make SamerArtisan Special
- Interactive Prompts
SamerArtisan makes user input effortless:
const name = await this.ask('Project name?');
const apiKey = await this.secret('Enter your API key:');
const framework = await this.choice('Choose framework:', ['React', 'Vue', 'Svelte']);
- Beautiful Output
this.info('✅ All systems operational');
this.warn('⚠️ Some warnings detected');
this.error('❌ Critical issues found');
this.table(
[‘Service’, ‘Status’, ‘Uptime’],
[
[‘Database’, ‘✅ Running’, ‘99.9%’],
[‘API’, ‘⚠️ Slow’, ‘98.1%’],
[‘Cache’, ‘❌ Down’, ‘0%’],
]
);
- TypeScript Support
Type-safe arguments and options right out of the box:
class TypedCommand extends Command<{name: string}, {force: boolean}> {
signature = "run {name} {--force}";
handle() {
const name = this.argument('name');
const force = this.option('force');
}
}
- Easy Organization
SamerArtisan
.load(['commands/project', 'commands/database'])
.parse();
Best Practices for CLI Design
✅ Make It Scriptable and Interactive
if (!this.option('yes')) {
const confirm = await this.confirm('Proceed with deployment?');
if (!confirm) return;
}
⚠️ Handle Errors Gracefully
try {
// logic
} catch (e) {
this.error(`Error: ${e.message}`);
}
🧭 Document Everything
SamerArtisan can generate help text directly from your command signatures — no extra effort needed.
Real-World Example: Database Migrations
Here’s what a realistic command might look like:
class MigrateCommand extends Command {
signature = `migrate
{--rollback : Rollback last migration}
{--steps=1 : Steps to rollback}
{--seed : Run seeders after migration}`;
description = "Run database migrations";
async handle() {
const rollback = this.option(‘rollback’);
const steps = parseInt(this.option(‘steps’) || ‘1’);
const seed = this.option(‘seed’);
<span class="k">if </span><span class="p">(</span><span class="nx">rollback</span><span class="p">)</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nf">runRollback</span><span class="p">(</span><span class="nx">steps</span><span class="p">);</span>
<span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nf">runMigrations</span><span class="p">();</span>
<span class="k">if </span><span class="p">(</span><span class="nx">seed</span><span class="p">)</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nf">runSeeders</span><span class="p">();</span>
}
}
Readable, structured, and clean — exactly how a CLI should feel.
When to Choose SamerArtisan
Use SamerArtisan if you’re building:
- A complex CLI with multiple commands or subcommands.
- A team tool that benefits from consistent structure.
- A TypeScript project where safety matters.
- A long-term project that will evolve over time.
Stick with Commander.js for:
- Quick scripts or small utilities.
- Projects with strict dependency policies.
- One-off tools where setup speed matters.
Final Thoughts
Building a CLI tool is about more than just parsing commands — it’s about creating a smooth experience for both humans and scripts.
- Commander.js is ideal for small tools and prototypes.
- SamerArtisan shines in structured, scalable, developer-focused projects.
At the end of the day, the “best” CLI framework is the one that fits your use case. Start simple, grow smart, and build tools that make developers’ lives easier — because the best CLI is the one people actually use.