How I built a single-command CLI to turn hours of machine setup into minutes—because life's too short to remember which scripts to run in which order.
Tony Kornmeier
You know that feeling when you put on a perfectly tailored jacket? Everything just fits. The shoulders sit right, the sleeves are the perfect length, you can move freely. That's how your development environment should feel.
Instead, most of us are working in the equivalent of borrowed clothes that almost fit. Close enough to get by, but never quite right. You switch machines and spend hours recreating your setup. You forget which scripts to run in which order. Your new laptop feels foreign for weeks.
It doesn't have to be this way.
For years, my dotfiles repository was a graveyard of good intentions:
bootstrap.sh for first-time setupinstall.sh for... something different?update.sh that I'd run every few months when I rememberedbin/ that did who-knows-whatSetting up a new machine meant running these scripts in a specific order I'd inevitably forget. Six months later, I'd be SSH'd into a fresh VM trying to remember: "Was it bootstrap then install? Or install then bootstrap? And do I need to source something first?"
Sound familiar?
I got tired of the guesswork. What I wanted was simple:
On a fresh machine:
curl -fsSL https://raw.githubusercontent.com/akornmeier/dotfiles/main/install.sh | bash
For daily updates:
dot
That's it. No flags to remember, no script order to memorize, no wondering if you ran the right thing.
I refactored the entire dotfiles setup into a single dot command with three subcommands:
dot bootstrapFirst-time setup. Installs Homebrew, clones the repo, symlinks everything. Run it once, forget about it.
dot installInstalls all your tools, apps, and dependencies from the Brewfile. Idempotent, so running it twice doesn't break anything.
dot updateKeeps everything current: pulls latest dotfiles, updates Homebrew packages, refreshes symlinks. This is the one you run regularly.
The magic part? Running dot with no arguments defaults to update. Because that's what you want 99% of the time.
The refactor added Changesets for automated version management and release notes. Now when I push changes, GitHub Actions handles the versioning and creates releases automatically. Zero manual bumping of version numbers.
I added prettier and oxlint to the project (and all the others) because even shell scripts deserve consistent formatting. When your dotfiles are the foundation of your dev environment, quality matters.
The old scripts still work if you've got muscle memory. They just call the new dot command under the hood. No breaking changes for existing workflows.
Before the refactor:
Total: ~2.5 hours, plus frustration
After the refactor:
Total: 30 minutes, zero frustration
The best tools are the ones you don't think about. Your text editor should feel natural. Your terminal should anticipate your needs. Your dotfiles should just work.
When you switch machines, you shouldn't spend hours recreating your environment. You shouldn't need to remember incantations or consult documentation. You run one command, grab a coffee, and come back to a machine that feels like home.
Since shipping this refactor:
New machine setup:
Easy updates:
dot periodically with my morning coffeeSharing with teammates:
For those who care about implementation:
Structure:
dotfiles/
├── bin/
│ └── dot # The CLI binary
├── script/
│ ├── bootstrap # First-time setup
│ ├── install # Package installation
│ └── update # Keep things current
├── install.sh # One-liner quick install
├── Brewfile # All the things
└── README.md # Actually kept up to date
The dot command is just a shell script that routes to the appropriate subscript:
dot bootstrap # Calls script/bootstrap
dot install # Calls script/install
dot update # Calls script/update
dot # Defaults to update
Simple. Obvious. Maintainable.
Most interactions are updates, not installs. Default behavior should match common usage.
Every command should be safe to run multiple times. No "only run this once" warnings.
Don't document the steps—automate them. Documentation gets stale, automation doesn't.
A new user should be able to figure out what to do without reading docs. If they can't, improve the tool.
I care about developer experience. My dotfiles should demonstrate that, not contradict it.
You spend 8+ hours a day in your development environment. It should feel like a well-tailored suit, not borrowed clothes.
When you switch machines, you shouldn't lose productivity. When you onboard a new team member, they shouldn't spend their first week installing tools. When you try a new framework, you shouldn't worry about dependency conflicts.
Your dotfiles are the foundation of your productivity. Treat them accordingly.
Want to see the setup? Check out the my setup.
Or just steal the pattern for your own dotfiles:
# Quick install
curl -fsSL https://raw.githubusercontent.com/yourusername/dotfiles/main/install.sh | bash
# Daily driver
dot
Because your development environment should just fit: reliable, familiar, and ready when you need it. Not something you fight with every time you need it.
Lately, I've been doing a lot of AI research and development. I've realized keeping my different machines sync'd with my custom Claude Agent skills and hooks I have developed is very tricky. I plan to automate the settings to be integrated into this setup. Stay tuned, I'll write a follow up article when I'm done.
Interested in more content on developer tools, automation, and building better workflows? Check out my open source projects or get in touch.