Shipping a Brew CLI in one week
17 Mar 2024

TLDR:

User enters typo command -> User says fuckoff -> command from terminal history goes boom!

# Installation via brew
brew tap Imgkl/fuckoff && brew install fuckoff

# Available Commands
fuckoff # deletes the last command.
fuckoff -a # deletes all history
fuckoff -i # interactive deletion mode

Have time to read? let's go.

The most annoying thing in terminal is, getting a typo in the command. The issue is now the typo command is stored in .zsh_history or .bash_history. Terminal autocompletion will suggest the typo command. I wanted to fix this.

There's a CLI called The Fuck, which you can enter to get proper suggestions on the previously entered typo command. but the problem is the typo command is still in history.

I want to write a CLI which can either clear the entire history or clear a specific command, including the CLI invoked command, becasue why not?

Introducing Fuckoff.

Day 1: Requirement Design

blog image

Click to view image in full screen

Day 2: Scripting

Looks like the easy way to get a CLI is through python scripting.

import argparse

def parse_args():
    parser = argparse.ArgumentParser(description="Manage your terminal history")
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-a", "--all", action="store_true", help="Delete all history")
    group.add_argument("-i", "--interactive", action="store_true", help="Selectively delete history items")
    return parser.parse_args()

def main():
    args = parse_args()
    if args.all:
       delete_all_history()
    elif args.interactive:
       interactive_delete() 
    else:
        delete_last_command()

delete_all_history() - Get the .zsh_history file and remove all of it's contents in write mode.

interactive_delete() - Read all the entries from the .zsh_history and using some library give a interactive space so the user can select the entries to be deleted.

Looks like the delete_all_history() method looks easy to implement, so let's work on that.

def delete_all_history():
    history_path = os.path.expanduser('~/.zsh_history')
    confirm = input("Are you sure you want to delete all history? This action cannot be undone. (y/n): ")
    if confirm.lower() != 'y':
        print("Operation cancelled.")
        return

    if os.path.exists(history_path):
        with open(history_path, 'w'):
            pass

        print("All history has been deleted.")
    else:
        print("History file not found.")
    pass

Note that this only works for .zsh_history, I will have to add support for .bash_history as well in the future.

Day 3: Interactive Delete

I will be using the questionary library to create an interactive CLI. The history entries will be read from the .zsh_history file and displayed as a checkbox list. The user can select the entries to be deleted and the selected entries will be removed from the history file.

def interactive_delete():
    history_path = os.path.expanduser('~/.zsh_history')
    if not os.path.exists(history_path):
        print("History file not found.")
        return

    with open(history_path, 'r', encoding='utf-8') as file:
        history_entries = [line.strip() for line in file.readlines()]

    selected_entries = questionary.checkbox(
        "Select history entries to delete:",
        choices=[{"name": entry} for entry in history_entries]
    ).ask()

    updated_history = [entry for entry in history_entries if entry not in selected_entries]

    with open(history_path, 'w', encoding='utf-8') as file:
        file.write("n".join(updated_history))
    if selected_entries.__len__() == 0:
        print("No entries selected.")
    else:
        print("Selected entries have been deleted.")

Now I have everything I need. Let's move to testing both methods.

blog image

Click to view image in full screen

blog image

Click to view image in full screen

Day 4: Pack it and ship it.

I will create a brew formula for the CLI and push it to a seperate GitHub repository. The formula will be added to the HomeBrew tap and the CLI can be installed using the brew install fuckoff command.

So I had to create a new repo for formula with a ruby file pointing to my python script repo.

class Fuckoff < Formula
  include Language::Python::Virtualenv

  desc "CLI tool to manage and clear Zsh history"
  homepage "https://github.com/Imgkl/fuckoff"
  url "URL TO MY RELEASE"
  sha256 "SHA256 of my release"

  depends_on "python@3.9"

  def install
     virtualenv_install_with_resources
  end

  test do
    system "#{bin}/fuckoff", "--version"
  end
end

And we are done. The Fuckoff brew package is now available for the public to use.

Yay.🥂

< Back