How to Build a CLI Tool as a Solo Developer
Step-by-step guide to building a CLI tool by yourself. Tech stack, timeline, costs, and practical advice.
What You're Building
A CLI (command-line interface) tool is a program that runs in the terminal. Think tools like git, npm, or docker. Developers love CLI tools because they're fast, scriptable, and composable. As a solo developer, building a CLI tool is one of the most satisfying projects you can take on. Small scope, clear functionality, and a developer audience that appreciates good tooling.
I've built a couple of CLI tools that automate parts of my workflow. They're the kind of project where you can go from idea to published package in a weekend.
Difficulty & Timeline
| Aspect | Detail |
|---|---|
| Difficulty | Easy to Medium |
| Time to MVP | 1-2 weeks |
| Ongoing Maintenance | Low |
| Monetization | Open source with sponsorships, or paid license for teams |
Recommended Tech Stack
For most CLI tools, I'd use either Go or Node.js/TypeScript. Go compiles to a single binary with no runtime dependencies, which makes distribution dead simple. Node.js is faster to develop with if you're already in the JavaScript ecosystem and can distribute via npm.
For Go, use Cobra (the library behind kubectl, Hugo, and GitHub CLI). Its latest release is v1.10.2 and it sits at about 44,000 GitHub stars, so it is the safe, well-trodden choice. For Node.js, use Commander.js or oclif. Commander is now on v15.0.0 and pulls roughly 390 million npm downloads a week, which tells you how battle-tested it is. oclif (the framework Salesforce and Heroku built their CLIs on) is on v4.23.8 with around 9,500 stars and about 303,000 weekly downloads, and it gives you more structure (plugins, generators) if your tool is going to grow into many subcommands. All three handle argument parsing, help text, and subcommands out of the box. Versions move, so check the current release before you pin.
Pinned Versions (Checked May 2026)
| Library | Ecosystem | Latest version | Reach |
|---|---|---|---|
Cobra (spf13/cobra) |
Go | v1.10.2 | ~44,020 GitHub stars |
Commander.js (commander) |
Node.js | v15.0.0 | ~390.9M npm downloads/week |
oclif (oclif) |
Node.js | v4.23.8 | ~9,533 stars, ~303K npm downloads/week |
chalk (chalk) |
Node.js | v5.6.2 | ~409.2M npm downloads/week |
fatih/color (fatih/color) |
Go | v1.19.0 | ~7,967 GitHub stars |
Step-by-Step Plan
Phase 1: Foundation (Week 1)
Define what your CLI does in one sentence. "It does X when you type Y." If you can't say that clearly, your scope is too big.
Set up the project with your chosen framework. Create the main command and 1-2 subcommands. Get argument parsing and help text working. Focus on making the "happy path" work perfectly before handling edge cases.
I always start by hardcoding values and getting the output right, then work backwards to make those values configurable via flags and arguments. It's faster than trying to design the perfect CLI interface upfront.
Phase 2: Core Features (Week 1-2)
Build out the main functionality. Handle errors gracefully with clear, helpful error messages. Add a --verbose flag for debugging. Add color output using a library like chalk (Node.js, currently v5.6.2 and around 409 million weekly npm downloads) or fatih/color (Go, currently v1.19.0 with roughly 8,000 GitHub stars) to make the terminal output readable. One thing to watch with chalk v5: it is pure ESM, so a CommonJS project on require() will fail to import it. Either move your project to ESM or pin chalk to the v4 line.
Write tests for your core logic. CLI tools are easy to test because inputs and outputs are well-defined. A function takes arguments, does something, and produces output. That's inherently testable.
Phase 3: Polish & Launch (Week 2)
Create a README with clear installation instructions, usage examples, and a few GIFs showing the tool in action. Terminal GIFs are incredibly effective for CLI tools. Use a tool like VHS (Charm's scriptable recorder, currently v0.11.0 and around 19,800 GitHub stars) or asciinema (currently v3.2.0 with roughly 17,300 stars) to record them. VHS is the one I reach for because the recording is a checked-in .tape script, so you can regenerate the GIF every time the UI changes instead of re-recording by hand.
If you used Go, set up GoReleaser to build binaries for macOS, Linux, and Windows automatically. The current stable release is v2.16.0 (about 15,800 stars). One gotcha worth knowing up front: as of GoReleaser v2.10 the old brews config block is deprecated in favor of homebrew_casks, since Homebrew now supports casks on Linux too. If you copy an old tutorial's brews: section you will get a deprecation warning, so use homebrew_casks: instead. Publish to Homebrew if your audience is on macOS. If you used Node.js, publish to npm. Make installation a single command.
Monetization Strategy
Honestly, most CLI tools are hard to monetize directly. The developer audience expects open source tooling to be free. But there are proven models that work.
Sponsorships. If your tool gets popular (thousands of daily downloads), GitHub Sponsors and Open Collective can generate meaningful income. Tools like Homebrew and curl sustain their developers this way.
Paid tiers for teams. Open source the core tool, charge for team features like shared configs, analytics dashboards, or priority support. This is how tools like Tailwind CSS monetize.
Wrapper SaaS. Build a web dashboard or API around your CLI tool. The CLI is free, the hosted service costs money. This is the Terraform model. Free CLI, paid Terraform Cloud.
The best approach is usually to build the tool, get it popular, and then figure out monetization. Trying to charge $5 for a CLI tool that nobody knows about doesn't work.
Common Mistakes to Avoid
Making installation complicated. If users need to install 3 dependencies before they can use your tool, most won't bother. Go's single binary approach shines here. One download, it works. For Node.js, make sure npx your-tool works out of the box.
Poor error messages. "Error: invalid input" tells the user nothing. "Error: expected a file path but got a URL. Did you mean to use the --url flag?" actually helps. Good error messages are what separate professional tools from hobby projects.
No documentation. Your README is your marketing page, your user manual, and your support system all in one. If someone can't figure out how to use your tool from the README in under 2 minutes, you'll lose them.
Overcomplicating the interface. A good CLI tool does one thing well. The Unix philosophy exists for a reason. Don't build a Swiss Army knife when people just need a screwdriver.
Common Errors and Fixes
These are the ones that bite almost everyone shipping their first CLI, with the fix grounded in the official tooling docs.
command not found after npm install -g your-tool, or the bin file will not run. This is almost always a missing shebang or a bin file without the execute bit. The first line of your entrypoint must be #!/usr/bin/env node, and the file needs to be executable. npm sets the executable bit for files listed in the bin field of package.json at install time, so the fix is to make sure your entrypoint is actually declared there ("bin": { "your-tool": "./bin/run.js" }) rather than relying on file permissions you set locally.
EACCES: permission denied when installing globally. This happens when npm install -g tries to write to a system directory like /usr/local/lib/node_modules without rights. Per the npm docs, the recommended fix is not sudo. Reconfigure npm to use a directory you own (npm config set prefix ~/.npm-global and add its bin to your PATH), or use a version manager like nvm so npm installs into a user-owned path from the start.
chalk import throws ERR_REQUIRE_ESM. chalk v5 is pure ESM. A CommonJS file using require('chalk') will crash. Either convert your project to ESM ("type": "module" in package.json and use import), or pin chalk to the v4 line, which still ships CommonJS.
GoReleaser warns that brews is deprecated. As covered above, v2.10 deprecated the brews section. Rename it to homebrew_casks, and where the old config said tap, the new one uses repository. For simple binary releases the swap is close to one for one.
Cobra flags read as empty. A frequent first-timer trip is binding a flag with cmd.Flags() but reading it before Execute() runs, or registering persistent versus local flags on the wrong command. Bind in init(), read inside the command's RunE, and use PersistentFlags() only for flags that should cascade to subcommands.
Is This Worth Building?
As a business? Only if it becomes popular enough for sponsorships or serves as a funnel to a paid product. As a portfolio piece and developer tool? Absolutely.
CLI tools get you noticed in developer communities. A well-built, useful tool can land you thousands of GitHub stars, which translates to credibility, job offers, and consulting opportunities. Some of the most well-known developers in the industry built their reputation on open source CLI tools.
The development effort is low, the learning is high, and the potential reach is massive. If you've been wanting to contribute to the developer ecosystem, building a CLI tool is one of the best places to start.
Sources
All versions, star counts, and download figures were checked on 2026-05-30.
- Cobra version and stars: github.com/spf13/cobra via api.github.com/repos/spf13/cobra (v1.10.2, ~44,020 stars)
- Commander.js version: registry.npmjs.org/commander/latest (v15.0.0)
- Commander.js weekly downloads: api.npmjs.org/downloads/point/last-week/commander (~390.9M)
- oclif version: registry.npmjs.org/oclif/latest (v4.23.8)
- oclif stars and weekly downloads: api.github.com/repos/oclif/oclif and api.npmjs.org/downloads/point/last-week/oclif (~9,533 stars, ~303K/week)
- chalk version and weekly downloads: registry.npmjs.org/chalk/latest and api.npmjs.org/downloads/point/last-week/chalk (v5.6.2, ~409.2M/week)
- fatih/color version and stars: github.com/fatih/color via api.github.com/repos/fatih/color (v1.19.0, ~7,967 stars)
- GoReleaser version and stars: github.com/goreleaser/goreleaser via api.github.com/repos/goreleaser/goreleaser/releases/latest (v2.16.0, ~15,823 stars)
- GoReleaser
brewstohomebrew_casksdeprecation (v2.10): goreleaser.com/deprecations and goreleaser.com/blog/goreleaser-v2.10 - VHS version and stars: github.com/charmbracelet/vhs (v0.11.0, ~19,813 stars)
- asciinema version and stars: github.com/asciinema/asciinema (v3.2.0, ~17,344 stars)
- npm EACCES global-install fix: docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally
- npm
binfield executable behavior: docs.npmjs.com/cli/v10/configuring-npm/package-json#bin
Like this? You'll like what I'm building too.
Two ways to support and get more of this work.
HEARTH
A privacy-first Life OS for your desktop. Journal, tasks, and notes that stay on your machine. Coming soon, direct download from this site.
Read moreMY TOOLKITS
Receipts-first toolkits for shipping after hours, building Claude agents, publishing on Amazon, and more. The exact methods I used, not theory.
Browse on WhopRelated Articles
Treating Video As Code With Remotion And fal
How I turned a script into a finished video with code instead of a timeline app, and why determinism is the real win for a solo dev.
Build Versus Buy For A Custom AI Feature
How to decide between an off-the-shelf AI tool and a custom AI feature, weighing control, cost, differentiation, and data with a clear framework.
Can One Developer Build Your Whole MVP
An honest look at whether a single full-stack developer can ship your whole MVP, when it works beautifully, and when you should bring in more people.