ℹ️ Note
It turns out that writing down my wishlist could speedup my adoption on new tools, so I refreshed my cli setup in 2026-01 and updated respectively. That being said, it’s unlikely to happen again in the near future.
Foreword
Just like Gentoo Revisited, I’m still distro-hopping to find the alternative distro for Void Linux and recently occupied with NixOS. While I started on nixos-unstable, NixOS-25.11 happened to just release a few days after I setup it. Perfect timing :) Anyway, I took the opportunity to finally do the migration from powerlevel10k (p10k) to starship as p10k is in maintenance mode to say the least. Also, last time I refreshed my p10k.zsh was 2022-09.
In my previous blog (made in bm) I published a post on 2020-04 (last updated on 2022-10) introducing how I follow the blog workflow to stay within terminal as much as possible. In dotfiles and mission-impossible I also hinted on my cli setup. As I sort of expose (read: leak) my dev workflow in Build the Ultimate Linter with Mise, an accompanied post about my current cli setup sounds quite reasonable to me so here it is.
I’ll also share some tips on optimizing zsh startup time to deprecate dotfiles#Improvement. TBA as it’s too much work. As my git extension and dev toolchain is revealed in mise-as-linter#TLDR, they are excluded on purpose in this post.
TLDR
Same
- Terminal: any
- Shell: Bash for
/bin/sh, Zsh for everything else - Editor: Vim
- TBB: w3m
- RSS: newsboat
- SSH: mosh+rlwrap ssh -> mosh + bare ssh
- File Browser: nnn
- Calendar: calcurse
- Music: cmus -> cmus + cmusfm
- Manual: tealdeer
- Format Converter: ffmpeg, ImageMagick/libavif, pandoc, dos2unix, chdman
Diff
- root shell: sudo + sudoedit -> sudo w/
sudo -e-> doas/OpenDoas +sudo -e-> sudo-rs1 - multiplexer: tmux + oh-my-tmux -> tmux
- pager: less + bat -> less -> moor
- ls: exa -> eza, tree -> eza –tree (lt)
- fetch: screenfetch -> neofetch -> screenfetch -> fastfetch
- top: bashtop -> htop -> htop-legacy + bottom (btm) -> htop + btm
- GNU grep -> ugrep -> ripgrep
- Checksum: sha512sum -> b3sum
- gzip -> pigz, zstd, zip/unzip native -> cosmocc
- cp -> install/rsync
- Data: jq -> jq + jc, visidata + sqlite-utils (+ nushell)
- Download: curl+wget -> wcurl, youtube-dl + you-get -> yt-dlp + you-get + gallery-dl
- Spellcheck: pangu -> autocorrect + vale
- cmp/xxd+diff -> xdelta3
- Podman -> LXC via incus -> QEMU/KVM via libvirt
- Email: afew + notmuch + mutt -> afew + notmuch + neomutt -> afew + notmuch + alot -> afew + notmuch + meli
Info
Same
As we are not SIXEL yet, I just pick up whichever available: Konsole, Windows Terminal, xfce4-terminal, GNOME Terminal. On main distro I use ghostty as Alacritty refuses to add image protocol, wezterm hardly tags release and kitty is too opinionated. It’s not a perfect replacement for qterminal but mostly works fine. Maybe I’d switch to foot once I finally migrate to Wayland.
Void links /bin/sh to dash for speed and FreeBSD defaults to tcsh/sh but I simply overwrite it to bash for maximum compatibility across systems. No, I don’t use fish even if it has excellent out of box experience and I think it’s quite easy to make a good non POSIX compliant shell. That being said, I’ve started the slow transition to nushell for data driven cross platform scripts where it suits better, to avoid the need of cosmopolitan bash.
I still use Vim as I still have not learnt how to use Emacs. I’ve heard of meow mode instead of evil mode though, maybe I’ll try it inside NixOS soon™.
W3M is still my favorite text-based browser, albeit poorly maintained. I also use chawan (written in Nim) and plan to fully embrace it once it’s more widely packaged.
I still use newsboat which is well maintained and highly configurable. W3M/Chawan is enough for simple sites and can show images right inside terminal.
I continue to use mosh for remote server and dropped rlwrap --no-warnings ssh for local connections as nowadays I tend to carry my dotfiles everywhere and rlwrap makes more trouble than the little convenience it brings.
For file browser, I still use nnn aliased to a single n with autocd on. Many good alternatives exist now, but I don’t have to the intention to migrate. On LFS I still use fff since it’s written in Bash.
I hardly use calcurse now as many events are managed via iCalendar, but it’s still there for private stuff.
I returned to Last.FM from Libre.FM since I don’t know when, because it just works™. For Libre.FM things are usually more complicated and I don’t bother to fix it. I still use cmus even though my issue regarding RIFF tag support opened a few years ago is still not replied, let alone getting a fix.
I still use tealdeer as tldr frontend and it seems it’s still well maintained and packaged by most distros.
I still use these tools to convert between different formats, just as millions of others in the world:
- ffmpeg: audio/video
- ImageMagick: image (libavif for AVIF encoding)
- Pandoc: markup text
- dos2unix: end of line (EOL)
- chdman: ROM
Recently I start to use libavif (avifenc) with GNU parallel to convert images into AVIF for speed. In rare cases, I would use vim :set encoding or iconv to convert file encoding. While Calibre is not a cli tool, I also use it to style books to my liking.
Diff
I migrated from sudo and sudo -e to doas/OpenDoas, only to discover that the persist feature is potentially unsafe on everywhere (including FreeBSD) except OpenBSD, and the lack of sudo -e is a big issue for me. When sudo-rs finally added sudo -e support in 0.2.8, I happily adopt it in the long last. On Alpine/OpenBSD I still use default doas and on other distros I also keep sudo as failsafe just in case.
I dropped oh-my-tmux and configure tmux once for all, after I checked the 1700+ lines of dirty code in oh-my-tmux. Zellij does not convince me, yet.
I migrated from less to moor (moar), although I still can’t see how it’s better. Syntax highlight powered by chroma is good, but I almost never do moor whatever.lua (I use vim for that) and only use pager in pipes. Also, with the migration I ditched colored-man-pages plugin in ohmyzsh and reimplemented it myself. I hardly use batcat nowadays, although I don’t know why. Sometimes I would use glow to render Markdown files but it’s also quite rare.
exa died at some point so I switched to community maintained eza. As tree is not always installed, I have alias lt="eza --tree" as failsafe.
Neofetch archived even before screenfetch developer declared its sunsetting, and now I use fastfetch. Still, on LFS I use screenfetch to avoid Rust toolchain.
Bashtop is hardly maintained so I switched to htop which works on Termux. Sadly at some point a new update of htop breaks rootless Termux compatibility, so I stick with htop-legacy on Termux and migrate to btm elsewhere. A few months later htop again supports rootless Termux so I revert the change. On other platforms I continue to use btm since it works fine. I’ve also used ctop for a while for containers but that may predate my last post as I already dropped Docker + lazydocker back then.
After breaking up with BusyBox grep on Alpine which does not recognize certain GNU grep parameter, I picked up ugrep (ug) because
- it’s the only 100% compatible implementation other than GNU grep itself back then (perhaps not the only one now)
- on
legacyenterprise level distro like Rocky9, the default GNU grep is outdated for years, ugrep nicely fills the gap - it has full Unicode support by default, which is used in my zsh plugin (via
zshaddhistoryhook) to avoid writing commands containing certain keywords and CJK characters toHISTFILE
However, ugrep seems to be removed in Rocky10/EPEL10 so I migrate to ripgrep (rg) when refactoring said plugin and just install it with mise github backend. Availability beats compatibility here.2
I prefer blake3 hash now as it’s fast, secure, and parallelizable. sha512sum is still good, but rather slow.
Similarly, pigz is gzip in parallel. Zstd is fast (see A Random Summary of Compressed Linux Backup, I also use it in zram). Zip/Unzip built with cosmopolitan libc works almost everywhere.
Sometimes install is better than cp as you can tweak permission/owner within the same command. rsync is fast and smart enough.
jc is used to convert cli output to JSON, which can be piped to jq and simplify things. I tried many cli tools that can read data from CSV, XLS, SQLite and visidata is so far the best one. With sqlite-utils I can easily publish data via datasette. As I mentioned above, I’m slowly adopting nushell for such tasks. For now I still use a mixture of them whenever appropriate.
curl now offers a wcurl script to have a similar experience like wget for lazy people like me, although it’s not well-known yet. On the contrary, everyone knows youtube-dl is killed by RIAA3 under the silly DMCA. gallery-dl is another cool tool like yt-dlp but for images and supports lots of sites.
I use autocorrect and vale to lint my prose, although it’s only used in my blog and not anywhere else.
xdelta is a good tool to make binary patch, useful in ROM hacking, although I have not made game localization patch with it yet.
I ditched podman to embrace incus at some point but I mostly just boot up a VM now. While Red Hat now endorses cockpit instead of virt-manager, I still prefer the latter as web interface just sounds off.
I still use notmuch plus afew to organize my emails, and moved from alot to meli because mailcap module is removed in Python 3.13. I dropped mutt/neomutt as it does not support reading .eml out of box (due to this the only emails I got are automated system messages/audit logs).
Easter Egg
As my dotfiles evolves, from time to time I’ll create/find aliases/functions with funny names, sometimes due to historical reasons, mostly for fun.4 Below are some examples.
- NB, not an acronym for nota bene or 牛 逼
, but an alias for
newsboat. - PC, not personal computer or 胖 次
, but an alias for
proxychains4(aka. proxychain-ng). - Oh, and GV is not Google Voice, definitely not Gay Video, but an alias for
TZ=UTC gpg --no-options --keyid-format long --verify.
NTR, not netorare (寝取られ) but an alias for the new nix cli or NixOS to do soft garbage collection:
# nix.zsh
if (( ! $+commands[nix] )); then
return
fi
if (( $+commands[nixos-rebuild] )); then
# NixOS only
alias ntr="sudo nix-env --delete-generations old && sudo nix-store --gc"
else
# nix cli
alias ntr="nix-env --delete-generations old && nix-store --gc"
fi
It derived from my apt alias atr for sudo apt autoremove and similarly:
- In Manjaro (pamac) it’s
ptrforpamac remove -o && pamac clean --keep 3 - and a function on Arch (pacman)
sudo pacman -Rs "$(pacman -Qdtq)" && paccache -r. - Of course, for Void (xbps) it’s
xtraka.sudo xbps-remove -R -oO.
Sadly g, gi are aliases for git/gitignore so I use etr on Gentoo (emerge/portage) for sudo emerge --ask --depclean && eclean --deep distfiles. Otherwise it would become a cool Nissan GT-R5 as well. Maybe I’ll introduce it once I get my hands on Guix someday.
I have many more funny examples but it seems I spend too much time on writing this (and Zsh part is even not included!) so let’s call it a day.
Now that I mention this, I have to complain about uutils-coreutils: it is SLOW, which is weird for a Rust rewrite. Compiling is much slower if I don’t want a dirty multi-call binary,
timeoutis significantly slower and it took me a few weeks to figure out that the 100ms latency in myperfectstarship config is caused by uutils… Compatibility would eventually become good enough, but performance issue really sucks. Either way, I’d still adopt it when it’s about time. ↩︎For whatever reason, ugrep only provides prebuilt binary for Windows so I have to compile from source wherever not packaged. While it’s not a big issue for me, it adds extra complexity when ripgrep works just fine. ↩︎
Recording Industry Association of America ↩︎
which also explains why my scoop bucket is named sb. You’ll get it if you understand why
ncfornatcatis funny. ↩︎Speaking of Japan, I also have
ssforslackpkg search, definitely not Shadowsocks or Schutzstaffel. ↩︎