bb-maid Gets a Major UX Upgrade: Install via bbin, Tab Completion and Cleaner Commands
Tonight was one of those productive coding sessions where small improvements snowball into something really polished. I’ve been working on bb-maid, a Babashka script that helps keep directories clean by automatically removing expired folders, and we made some significant UX improvements that I’m excited to share.
What is bb-maid?
Before diving into the changes, let me quickly introduce bb-maid. It’s a Babashka script that works like a “digital housekeeper” for your filesystem. You create files named cleanup-maid-YYYY-MM-DD
in directories you want to expire, and bb-maid will prompt you to delete those directories once the date passes. Think of it like expiration labels on food containers, but for your code directories.
The Journey: From Working to Delightful
Phase 1: Making it bbin-Compatible
The next challenge was making bb-maid installable via bbin, Babashka’s script manager. This required some structural changes:
Created proper namespace structure:
;; src/simonneutert/bb_maid.clj
(ns simonneutert.bb-maid
(:require [bling.core :refer [bling callout]]
[babashka.fs :as fs]))
(defn -main [& args]
;; Command handling
)
Added dependency management:
;; deps.edn
{:paths ["src"]
:deps {io.github.paintparty/bling {:mvn/version "0.4.2"}}}
Configured bbin entry point:
;; bb.edn
{:bbin/bin {bb-maid {:main-opts ["-m" "simonneutert.bb-maid"]}}}
Now users can install with a single command:
bbin install io.github.simonneutert/bb-maid
Phase 2: Adding the clean-in
Command
The first improvement was adding a clean-in
command to make it easier to create cleanup files:
bb-maid clean-in 7d
This parses duration strings (like 7d
, 30d
, 90d
) and creates a cleanup-maid-YYYY-MM-DD
file with the appropriate expiration date. No more manual date calculations!
Key features:
- Natural duration syntax (
1d
,7d
,30d
, etc.) - Automatically replaces existing cleanup files (preventing duplicates)
- Styled terminal output using the bling library
Phase 3: Adding Professional Tab Completion
This was the big one. Modern CLI tools need great tab completion, and we added support for both zsh and bash.
The Tab Completion Challenge
The first attempt had the completion file named bb-maid.zsh
, but zsh wasn’t loading it. After some debugging, I discovered that zsh completion functions must be named with an underscore prefix (e.g., _bb-maid
). This is a zsh convention that tells the completion system to auto-load the function.
What Gets Completed
The completion system is context-aware:
bb-maid <TAB>
→ Showsclean
andclean-in
commandsbb-maid clean <TAB>
→ Shows available directoriesbb-maid clean-in <TAB>
→ Suggests common durations (1d, 7d, 14d, 30d, 60d, 90d)
Phase 4: Cleaning Up the Command Structure
The final improvement was prompted by a great question: “Wouldn’t bb-maid clean /path/to/dir
be cleaner?”
Absolutely! The original API was:
bb-maid /path/to/dir # Implicit clean command
bb-maid clean-in 7d # Explicit command
This inconsistency was confusing. Now it’s:
bb-maid clean /path/to/dir # Explicit clean command
bb-maid clean-in 7d # Explicit command
Much more intuitive and consistent with modern CLI conventions!
Technical Lessons Learned
1. Zsh Completion Naming Convention
Completion files must start with an underscore (_bb-maid
) for auto-loading to work. This is a zsh convention that’s easy to miss if you’re new to writing completions.
2. Glob Qualifiers for Robust Paths
Using (N)
in zsh glob patterns makes them optional:
fpath=(~/.gitlibs/*/completions(N) $fpath)
Without (N)
, zsh will error if the path doesn’t exist yet.
3. bbin Requires Proper Structure
For bbin compatibility, you need:
- A
deps.edn
with dependencies - Proper namespace structure with
-main
function :bbin/bin
configuration inbb.edn
- Java/JDK for dependency resolution
4. Context-Aware Completion is Delightful
Taking the time to make completion suggestions contextual (directories after clean
, durations after clean-in
) makes the tool feel polished and professional.
Read next ➡️