Code and Life

Programming, electronics and other cool tech stuff

Supported by

Supported by Picotech

Turn Comments into Fish Shell Commands with OpenAI

Notice: I wanted to see if OpenAI canvas can do reasonable Markdown editing, so this post is co-written with ChatGPT 4o with Canvas. The code and Fish script were done before writing this separately with the gracious help of our AI overlords as well. I've kept the prose to minimum and edited the result myself, so benefit should still be high, even though manually written content is low.

Recently, I wanted to make my command-line experience a bit more conversational. Imagine writing a comment like # list files, pressing enter, and seeing it magically turn into the corresponding Fish shell command: ls. With OpenAI's API, this becomes not just possible but surprisingly straightforward. And should rid me of jumping to ChatGPT every time I need to remember how find or let alone ffmpeg exactly works.

This blog post walks through creating a Python script called shai that turns natural language comments into Unix commands using OpenAI's API, and then utilizing that script with a Fish shell function to replace a comment written on command line with the actual command. After the command is generated, you can edit it before running it — a nice way to integrate AI without losing control.

Setting up the environment

Before we dive into the script, make sure you have the following:

  1. Python installed (version 3.8 or higher is recommended).

  2. An OpenAI API key. If you don’t have one, sign up at OpenAI.

  3. The OpenAI Python library in a Python virtual environment (or adjust the code below if you prefer something else like pip install openai on your global env):

    $ python3 -m venv /home/joonas/venvs/myenv
    $ source /home/joonas/venvs/myenv/bin/activate.fish # or just activate with bash
    $ pip install openai
    
  4. A configuration file named openai.ini with your API key and model settings, structured like this:

    [shai]
    api_key = your-openai-api-key
    model = gpt-4o-mini
    

The Python script

Here’s the Python script, shai, that interprets natural language descriptions and returns Unix commands:

#!/home/joonas/venvs/myenv/bin/python

import os
import sys
from openai import OpenAI
import configparser

# Read the configuration file
config = configparser.ConfigParser()
config.read('/home/joonas/openai.ini')

# Initialize the OpenAI client with your API key
client = OpenAI(api_key=config['shai']['api_key'])

def get_unix_command(natural_language_description):
    # Define the system prompt for the model
    system_prompt = (
        "You are an assistant that converts natural language descriptions of tasks into "
        "concise, accurate Unix commands. Always output only the Unix command without any "
        "additional explanations or text. Your response must be a single Unix command."
    )

    # Call the OpenAI API with the description
    response = client.chat.completions.create(
        model=config['shai']['model'],
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": natural_language_description},
        ],
        temperature=0,  # To ensure consistent and accurate output
    )
    
    # Extract the command from the response
    command = response.choices[0].message.content.strip()
    return command

def main():
    if len(sys.argv) < 2:
        print("Usage: shai <natural language description>")
        sys.exit(1)
    
    # Get the natural language description from command line arguments
    description = " ".join(sys.argv[1:])
    try:
        # Generate the Unix command
        unix_command = get_unix_command(description)
        print(unix_command)
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()

How it works

  1. Configuration: The script reads an openai.ini file for API credentials and model settings.
  2. Command generation: When you provide a natural language description, the script sends it to OpenAI’s API along with a system prompt specifying the desired output format.
  3. Output: The script returns the corresponding Unix command.

You could place it in e.g. ~/bin and do chmod +x shai to make it runnable, and then test it:

$ shai list files
ls

Extending to Fish shell

To make this functionality seamlessly available in the Fish shell, you can use the following Fish function:

function transform_comment_line
    set cmd (commandline)
    # Check if line starts with a hash (a comment)
    if string match -q "#*" $cmd
        # Remove the '#' and possible leading space
        set query (string trim (string sub -s 2 $cmd))

        # Run your "shai" script (replace 'shai' with the actual command)
        # Assuming that 'shai' takes the query as arguments and prints the command
        set result (shai $query)

        # Replace the current command line with the output of 'shai'
        commandline -r $result
        # Now your command line is replaced with the generated command.
        # The user can edit it further if needed, and press Enter again to run.
    else
        # If it's not a comment line, just execute normally
        commandline -f execute
    end
end

Save this function in your Fish configuration directory as .config/fish/functions/transform_comment_line.fish. Then, bind it to a key or trigger it manually to convert comments into executable commands. I am using this in my .config/fish/config.fish to automatically run on enter:

if status is-interactive
    # Commands to run in interactive sessions can go here
    bind \r transform_comment_line
end

And that is literally it. Enjoy!

Ending was edited for brevity, ChatGPT wanted to rant on how this could become a powerful part of your workflow...