Fzf As A TUI For Your Tools
So recently I’ve been playing around with an idea of a CLI application, distributed as a GitHub CLI extension, that would allow me to quickly lookup the README of some repository that I’ve starred on GitHub without leaving the command line. I often find myself wanting to do this, for example when I’m working on my Neovim configuration or when I’m trying to remind myself of some command alias from a Zsh plugin.
In this post I want to write about how I’ve been approaching this using the command line fuzzy finder fzf.

# Fzf As A Git Picker
The fzf
CLI is a great tool for searching a list of strings interactively and I’ve been using it for a while now as a picker for git branches in code bases at work.
For example, this simple bash function that I’ve aliased to a command in my shell configuration gives me a lot of value when I want to checkout a remote git branch that matches the name of some issue on my team’s Kanban board:
The commands below lists all the remote branches in the branches
variable,
pipes it to fzf
and finally captures the selected list item in the selection
variable
using fzf
.
branches=""
selection=
Here I simply remove any remote name prefix, like origin/
, using the Perl regex engine, and switch to the selected branch:
if ; then
| |
fi
# Fzf’s CLI
Combined with the GitHub CLI gh
, which allows you to easily send authenticated requests to the GitHub GraphQL API, I thought it would be interesting
to try and write a small bash
program which would allow me to fuzzy search for a starred repository and then display its README in a pager or editor.
This resulted in me writing gh-starred, a multi-file bash
script that uses fzf
together with the GitHub CLI gh
.
Before starting this project, I had the thought that writing this straight up in bash
might not be the most
practical or ergonomic way to do this, but I saw it as a fun challenge to see if I could learn some new things about bash
in the process. It came to a point where I was reaching for something like a map data structure
in order to map GitHub repository names to GitHub object IDs. This led me to discover that
bash
has associative arrays, which I did not previously know about.
Ultimately, I did not need to use associative arrays,
but instead I could make use of the excellent CLI that fzf
provides through its options.
See, my approach was to display the repository information as tabular data,
with some obvious columns like nameWithOwner
, description
and url
.
Fzf provides options like --nth=N
, which allows you to specify which column
should be used in the fuzzy search algorithm. In my case that would be
the nameWithOwner
column by default.
Now together with the option --delimiter=STR
that instructs fzf
how to
parse the columns in your tabular data, and the option --with-nth=N
which
tells fzf
how many of the columns you want to display in the TUI,
you can have hidden columns in each row, e.g. id
.
When the user selects one of the rows in the fzf
TUI, the value in the hidden column
of that row can then be captured (or another value from any other column for that matter)
and passed to another command in your script.
This is enabled through the highly flexible binding interface that fzf
provides
with the --bind '<KEY|EVENT>:<ACTION>'
option, where field (column) values can be
referenced in the ACTION
syntax.
As the amount of combinations of arbitrary actions you can perform with this interface
is incredible generous, I will not cover many of them here. But for instance this allows
me to capture the internal GitHub id
of the selected repository
and then use it to fetch the README of that repository through another call to the
GitHub GraphQL API and display it with an arbitrary shell command, using the
become()
action among fzf
’s multiple actions.
# Iterating on Fzf
My initial implementation of gh-starred
simply uses these CLI bindings to synchronously
fetch data from the GitHub GraphQL API and update the TUI by invoking the fzf
program again with new data.
However, this does not feel as snappy as I’d like a CLI application to feel like.
A solution to this issue that fzf
seems to provide, is the ability to
start a HTTP server using the --listen=HTTP_PORT
option, which allows you to POST
new data and update the TUI asynchronously.
This have inspired me to rewrite gh-starred
in order improve upon the snappiness.
I also want to support pagination, which it correctly does not and is a big limitation.
Finally I would like to remove the dependency on jq
, as this was something I used
mainly for my own ergonomics when developing the script.
I’ve been looking at babashka as an implementation candidate over bash
,
as I’ve been trying to get into Clojure lately and I feel like the language provides ergonomic destructuring out-of-the-box,
similar to jq
, in addition to allowing me (I think?) to compile the project to a single binary
using GraalVM native-image.
I hope this post has inspired you to try out fzf
in your own CLI applications.
More on gh-starred
in babashka
perhaps in future blog posts.