webcodr

More awesome CLI tools

This is a follow-up article to Awesome CLI tools with even more useful CLI tools.

btop

It’s top on steroids with a beautiful terminal UI. Way more information at a glance and just a joy to use.

delta

Do you often look at git diffs? Are you missing syntax highlighting? delta is here to help. Just install it, configure delta as pager for git and voila, diffs with syntax highlighting.

dog

Dog is a modern DNS lookup tool with colored output and a simple interface.

tokei

Are you a fan of statistics? Tokei can summarize file types, lines of code, comments or blank spaces of your project.

I’m currently converting a large Java project to Kotlin and tokei makes it easy to see the progress.

procs

A modern replacement for ps with colored output, keyword search, tree view and some useful helpers for networking (ports) and Docker.

lazygit

One of my favorites. Lazygit is a dead simple, yet powerful and elegant to use terminal UI for git. I’m not a fan of the git CLI and always preferred a GUI tool like Tower, but nowadays lazygit is my git client of choice. I don’t think there is a better git client out there. Even many NeoVim distributions support it out-of-the-box (LazyVim with LEADER + gg).

sd

Do you use sed? Do you like it? I don’t. sd is better, much better.

I'm using Arch btw

Well, that escalated quickly. One week ago I would have said never to touch Arch (except SteamOS). Nothing personal, it’s just that I am primarily a Mac guy and never stayed with Linux that long. I really enjoyed Pop!_OS. System76 created a really nice flavor of Ubuntu, but it’s still Ubuntu, so packages are often out of date and you always have to rely on custom repos or other means to get current versions.

With Arch and its rolling release model the latest versions are almost immediately available. Sounds good? Of course there’s a catch: breaking changes are also immediately available. Updating all dependencies can break many things at once and that can get very annoying pretty fast. From what I’ve heard the situation improved over the years, so if you’re doing nothing too crazy, it should be okay.

A few days ago I discovered Omarchy from DHH (creator of Ruby on Rails). He created an opinionated Arch setup for developers. Just install Arch via archinstall and run Omarchy’s installation script and you’re mostly done. A few days ago Omarchy version 2.0 was released and brings a complete ISO file with its own installer. Just boot the ISO from a USB drive, enter some things like your user name, password, hostname etc. and wait a few minutes. That’s it!

Omarchy ships with Hyprland, a really efficient and fast tiling Wayland compositor that doesn’t look like it’s from the 1980s. There’s all sorts of eyecandy, everything is customizable and there are also many cool plugins.

Setting up Hyprland and plugins on your own can be a daunting task and a very time-consuming one as well. Omarchy to the rescue! It ships with a really good default configuration and is perfectly usable from the get-go, but that’s only the beginning of the journey. DHH considers Omarchy a starting point for your own configuration and therefore made it very easy to customize the configuration for your needs. There is even a special Omarchy configuration tool for switching the global color theme (for Hyprland, Alacritty, Neovim etc.) or setting up fingerprint sensors for biometric authentication (should be working very well with Framework devices).

Omarchy also installs many useful terminal and GUI programs, like Alacritty (terminal emulator), Neovim, Spotify, Lazygit, fd etc. All GUI programs are mapped to intuitive hotkeys, f.e. SUPER + M for Spotify or SUPER + B for the browser (Chromium by default). SUPER + Space displays the application launcher (like a simple version of Spotlight or Raycast). There’s much more for controlling the current window size to move the focus, switch virtual desktops, fullscreen etc.

All hotkeys are easily customizable in .config/hypr/bindings.conf. You don’t like Chromium? Just install your favorite browser with Pacman (Arch’s package manager) and update the variable $browser in the Hyprland config. That’s it. The browser is also used for webapps mapped on hotkeys, ChatGPT f.e. is mapped on SUPER + A.

A little example to change the music hotkey to Apple Music as webapp (there is no native Linux version).

bindd = SUPER, M, Apple Music, exec, $webapp="https://music.apple.com/de/library/recently-added?l=en"

Pressing SUPER + M now opens a browser window in app mode (no address bar etc.) with the Apple Music website. Nice. I also changed SUPER + A from ChatGPT to Theo Browne’s awesome T3.chat.

That’s far from all that Omarchy offers, but would be a bit much to list every feature in this blog post. Omarchy has a pretty good documentation with many useful tips, a troubleshooting section etc. They even have advice on my problems with Bluetooth. The Minis Forum UM760 comes with a MediaTek WiFi card and sadly MediaTek doesn’t offer Linux drivers for this particular model. I switched to an Intel AX210 card for a few bucks and now it works like a charm.

According to DHH they will even ship an ISO file with Arch and a custom installer in the near future. It’s also planned to switch the default terminal from Alacritty to Ghostty, but there are still some issues to solve.

TL;DR

If you’re not afraid of Linux and want a decent base installation of Arch with many developer-focused features, give Omarchy a chance!

Updates

  1. 2025-08-05: Omarchy 1.11 brings a new Hyprland config file structure and bindd command. I updated the file path for key bindings and the example for Apple Music accordingly.
  2. 2025-08-28: a small update about the release of Omarchy 2.0 with its own ISO and installer.

Please don't do this with switch statements

The classic C-like switch statement is fine, but it has its flaws. It’s no coincidence that modern languages like Kotlin or Rust offer alternatives like when or match or a more fine-tuned version of switch like Zig.

I’m currently in the early stages of rewriting a large and complex Java code base to Kotlin. Some parts of this codebase are really ugly and unnecessarily complicated and convoluted. Yesterday I crossed the path of a nasty use of a switch statement in an operation on a Java Stream. Unfortunately I can’t share the real code, but imagine something like this:

int value1;
int value2;

for (...) {
    switch (enum) {
        case Enum.FOO:
            if (something) {
                if (somethingElse) {
                    value1 = someConvolutedStreamOperations();
                    value2 = someOtherConvolutedStreamOperations();
                    // imagine 30 more lines here
                } else {
                    value2 = 0;
                }

                break;
            }

        case Enum.BAR:
        default:
            value1 = 1;
            value2 = 2;

            break;
    }
}

This was part of a Java class with over 1,000 lines of code. Streams with many operations everywhere and sometimes very deep nesting thanks to old-style Java code. The original case for Enum.FOO stretches over almost the whole display space, so it’s not easy to spot any potential pitfalls. After I ran IntelliJ’s Kotlin migration tool and cleaned up all errors, it was time to run the unit tests and four out of 24 failed.

As you can imagine it’s not straight forward to find problems in such a large and complex class but I came across a notice of an unused value assignment. Why it was there was also not clear immediately, so I compared the original Java file with the Kotlin version.

Since Kotlin has no switch IntelliJ converted it to when. Unlike its Java counterpart when can handle null and is exhaustive with enums. There is also no break keyword and that’s exactly the problem here.

Look at the Java code and think about what happens on Enum.FOO if something is true but somethingElse isn’t. The break keyword is not triggered and the switch statement goes on to the default block. As a result value1 and value2 are assigned their values.

IntelliJ’s migration tool is quite good, but it didn’t catch that and the generated when statement was wrong. That’s also the reason for the unused assignment notice. I assume the migration tool was confused by the scope of break since both switch and for can use it. Also there is no real match in Kotlin for such a structure. You have to assign the values of both variables for every branch inside the when statement. After fixing that all tests ran successfully.

It’s a really good example of how not to use switch statements, especially with nested control structures in a case. Since the original author of the code is no longer available, I can only guess why it was written this way. Probably to avoid duplication or even unintended. Well, it could seem as an elegant solution if you’re familiar with the code, but to other people it’s just an unnecessary pitfall that should be avoided. It’s unintuitive at best, not easy to comprehend and can lead to nasty bugs, especially if it’s unintended behaviour. That’s why Kotlin, Rust, Zig and other modern languages avoid breaks in their alternatives to switch statements in the first place.

Fixing no A2DP with Bluetooth headsets on Linux

Please beware that the following instructions are suitable for media consumption only! After these changes your headset can’t make any calls without a dedicated microphone until you undo them.

Having trouble with the audio quality of your Bluetooth headset on Linux? It sounds awful if you’re listening to music and videos? Well, congratulations, I had the same problem and found a solution. At least if you’re only into listening and don’t make any calls. This works on Ubuntu or Ubuntu-based distributions like Pop!_OS or any other distribution that relies on BlueZ and Pipewire/WirePlumber for Bluetooth audio.

What’s wrong?

Bluetooth has different profiles for different things. If you want to make a call, your headset will switch to the Hands-free Profile (HFP). The available bandwidth will be shared for audio input and output and different audio codecs will be used. It’s good for calls, but really shitty if you want to listen to music. The headset needs to switch to A2DP (Advanced Audio Distribution Profile) for good sound quality. This should happen automatically and HFP should only be active, if you’re making a call. I never had trouble on macOS or Windows with this, but I’m trying Pop!_OS now. It worked for a few days, but today the headset would only connect with the HFP and streaming music or watching videos was as pleasant as dental treatment with a power drill.

Many searches later, I found out that WirePlumber (the session manager for the Pipewire multimedia framework) has some bugs that will trigger HFP on BT headsets even if there’s no call. That’s pretty annoying but at least somewhat easy to solve. There’s a solution in the Arch Linux Wiki, but it needs some modifications for Ubuntu-based distributions.

The solution

First you need to create a directory path in your home directory:

mkdir -p ~/.config/wireplumber/bluetooth.lua.d/

This will create a directory that allows you to override the default WirePlumber Bluetooth config without overwriting the original file.

Now copy the original config to the override directory:

cp /usr/share/wireplumber/bluetooth.lua.d/50-bluez-config.lua ~/.config/wireplumber/bluetooth.lua.d/

You can now edit the file in the override directory.

Beware that the original instructions from the Arch Linux Wiki contain a conf file, but at least with Ubuntu (and Pop!_OS) the config file is written in lua, so it’s a completely different syntax.

Look for bluez5.roles – it should be commented out. I would recommend not to replace the comment and just put the following line below. It’s easier to undo if something goes wrong or you need to enable HFP.

["bluez5.roles"] = "[ a2dp_sink a2dp_source ]"

Save the file and restart the Bluetooth service:

sudo systemctl restart bluetooth

Now reconnect your device and A2DP should be working fine.

Awesome CLI Tools

There are some incredibly useful CLI tools out there. Here’s a list with some awesome tools I’m using for my daily work.

atuin

Atuin is a history replacement with a fuzzy finding search and sync/backup options (self-hosted if you need). It’s written in Rust (blazingly fast!) and stores the history entries in a SQLite db. You can even import your current history from your shell. Atuin supports bash, zsh, fish and NuShell.

bat

Need cat a lot? Bat is a cat clone on steroids with syntax highlighting, themes, git integration and much more.

eza

Everyone needs ls ? Nope, eza is much better. Colored output, icons via nerd fonts, git status tracking per file, tons of display options …

tldr

Reading a man page can be frustrating, why can’t I just have the TL;DR version? Well, tldr does exactly that. Just use it like man and enjoy the TL;DR version of a man page.

zoxide

Like its siblings, cd is a little dated and clunky. With zoxide you can easily jump to directories without typing the full path. It stores a history of your visited paths and you can jump via keywords to your directories.

chezmoi

You’re using multiple computers or just want a simple and reliable way to store your dot files? Chezmoi is your friend and stores your dot files in a git repo with syncing capabilities to other devices. It’s even possible to encrypt your files. If you have secrets in your dot files, chezmoi comes with integrations for many password managers to safely store your passwords, tokens etc.

starship

Your shell looks boring? Just theme it with starship! It’s pretty easy to build your own and if you don’t want to, there are many themes available. Starship also has integrations for many dev tools to show the current git status or currently active versions of your runtime environments like NodeJS, Rust, Go, Java etc.

fzf

Finding files with the usual suspects works fine, but fzf can do this faster and much more intuitive. It’s a fuzzy finding search within your current directory, processes, git commits, history (if you don’t like Atuin) and much more.

ripgrep

Ripgrep is a really fast regex-based search tool and can do much more than grep alone.

btop

Another top variant? Yup, but btop is way more like its modern GUI-based colleagues on macOS or Windows with CPU and GPU usage, process trees, I/O and disk activities, battery status …