Navi-A-Cheatsheet-for-CLI

Navi: A “Cheatsheet” CLI

Jake Everhart Programming Leave a Comment

The command line is a powerful tool in software development. Regardless of whether you are comfortable working within a shell or if you prefer a GUI, there are likely scenarios that still require you to open a terminal and type out some commands.

This should not surprise us – many tools solely support Command-Line Interface (CLI) interaction – but over time, the efforts required to keep track of each new command’s syntax can add up. If you’re anything like me, you might memorize only the commands that you use most frequently, resigning yourself to searching through documentation for the rest whenever you need them.

This problem of periodically needing to hunt down command examples has several solutions: browser bookmarks, terminal history, --help flags, asking your favorite LLM, etc. There are even existing command-line utilities like cheat.sh or TLDR, which provide access to community-maintained “cheatsheets” without needing to leave your terminal.

Today, we will be looking at Navi, an alternative that can streamline this cheatsheet flow even further.

What Is Navi?

Navi’s documentation describes it as an interactive cheatsheet tool for the command line. Its philosophy is simple: leverage the same cheatsheet syntax as similar tools with an enhanced focus on interactivity. This means that, while you could just use it to search through command snippets and browse examples, it also provides some additional benefits:

  • Interactive fuzzy finders
  • The ability for cheatsheets to accept parameters
  • Easily-enabled shell integration

The Navi repository’s documentation includes a video showcasing these features in action, but I included a screenshot below for the one I found to be the most impressive: initiating a shell session within a running Docker container.

Navi repository’s documentation

As shown in the videos, the user types in the query of shell container and then presses a key combination to invoke Navi for their current input string. Navi then locates the cheatsheet that best fits their query (shell into container [docker]) and prompts them for the container_id value to use with the command.

Since that cheatsheet also included a way to supply options to the user, a fuzzy finder is provided for the user to select which container they wish to engage with. And once the user selects a container, the corresponding container ID is populated into the cheatsheet command, and the resulting command is passed back to the shell for the user to execute.

Now, is the command in that example impossible to memorize? No, of course not. Developers who regularly work with Docker could even have an alias configured to accomplish the same task. The impressive piece is the versatility of this interaction. When I watched this video, I recognized that a workflow like this could be a huge asset for some of the tools I use…as long as it was easy to set up and configure.

So, how about we test that out?

Getting Started

Navi’s installation process was pretty painless. I elected to use cargo since I already had Rust installed on my system, but it’s also available via Homebrew, Chocolatey, or some other package management systems. After installation was complete, the first invocation prompted me to automatically download a set of default cheatsheets. With this, I was already able to query and select from the commands present in the repository that I had picked.

The next piece to enable was the shell integration, as I was particularly fond of the “type a query and have Navi replace it” scenario shown in the video. Here again, this was straightforward. Since Navi exposes a widget subcommand for displaying integration scripts for popular shells, I was able to run navi widget zsh and save its output to the directory where I store my custom Zsh scripts. This established Ctrl+G as a hotkey for invoking Navi on any future shell sessions.

As far as meta-command utilities go, that setup process wasn’t so bad! However, this just gives us access to the “featured” cheatsheets that have been blessed by the authors. This is certainly still useful – there are plenty of commands available in these community-maintained repositories – but how difficult is it for me to hack together my own?

Adding Our Own Commands

Running navi info cheats-path tells us what directory it searches for these cheatsheet files. This means that we can write our own expansions by just creating our own files in a directory under that path. Let’s do this for a command that I frequently forget how to use: jq.

For those who don’t know, jq is a command for processing and manipulating JSON data. In addition to the actual JSON text, it’s also (typically) given another parameter that dictates what processing it needs to perform. That parameter takes the form of a (possibly very long) string, which jq then interprets as a filter to be performed upon the given JSON data. The jq tutorial gives us the following example to reference:

curl 'https://api.github.com/repos/jqlang/jq/commits?per_page=5' | jq '.[0] | {message: .commit.message, name: .commit.committer.name}'

The above command makes an API call to retrieve a part of the jq commit history as a JSON payload and then pipes that payload as text to the jq command. Then, the jq filter string selects the first item in that JSON array and outputs a JSON object consisting of the commit message and the committer’s name. While this example isn’t overly complicated, it illustrates how jq filters can be chained together in order to perform complicated tasks.

This chaining, or the various pieces of syntax involved, is the part I find myself hunting down examples of. I can never seem to dedicate the important subcommands to memory, or internalize where I define the new name of a field versus the path for populating it. But since these filters revolve around chaining piped sections together, we can write the following script as an example of offloading this memorization to Navi:

% jq, json
; These are tags, which help Navi narrow down which cheatsheets to pull from based on your query

# Start query
jq '. | 
; Note: The line above is a deliberate no-op, but Navi treats '|' as a delimiter for the next search string

; Items below are intended as incremental building blocks for JQ filter strings

# Access the value at key
.<key>

# Access first list item
.[0]

# Slice array
.[0:1]

# Extract all keys from json
keys

# Sort by a key
sort_by(.<key>)

# Count elements
length

# Print only selected field
{<field>}

# Map field to property
{<new_field>: <field>}

# Select records matching criteria
select(.<field> == "<value>")

Once I save the above script into my Navi directory, I can now build up my filter string by doing the following:

  1. Write the same curl command from before, with a pipe at the end
  2. Type jq start and enter my Navi hotkey to auto-replace it with our starting syntax
  3. Type jq first and hit the hotkey, getting the syntax for selecting the first array item
  4. Type jq map and enter the names of the input and output JSON fields desired
  5. End the filter string with a single quote and run the command

As complicated as this looks when written out, the user experience is surprisingly intuitive:

Navi user experience

Effectively, I now have a way to describe into my terminal what I’m trying to accomplish with jq, and it will help me get to the finish line! There are certainly more pieces of the filter language that I could add to my cheatsheet over time, but this seems like a good stopping point for our example.

Conclusion

The idea of terminal-centric “cheat sheets” is nothing new, and their usefulness isn’t terribly surprising. However, Navi’s interactive and easily-expanded presentation makes their argument even more compelling. Augmenting your arsenal with submissions from the community seems like a good way to produce a reliable, local reference for common commands. When paired with the ability to put together searchable templates for any arbitrary commands, it becomes too good to pass up!

This test run has convinced me to add Navi to my toolbelt, and if you find yourself working in the command line on a regular basis, it’s definitely worth taking a look.

Have an opinion or an alternative method? Drop a comment below, and check out the Keyhole Dev Blog for more content.

5 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments