iTranslated by AI
I Built a CLI Tool to Use ChatGPT Directly from the Terminal
Introduction
ChatGPT is quite popular, isn't it? I started using it once GPT-4 became available. As I used it, I felt that it would be more convenient to use it from the terminal without stress, so I decided to create a command-line interface (CLI) tool.
Here's what the actual operation looks like:

The web version of ChatGPT (https://chat.openai.com) is convenient, but I had a few frustrations:
- I have to open a browser.
- It's hard to organize when I start too many new chats.
- It's difficult to view chat history across multiple chats (e.g., fuzzy search).
- The browser editor is inconvenient.
- There was a period of about two days when chat history was inaccessible due to an outage.
- Input from sources other than the API (like ChatGPT) is used for training purposes.[1]
- Depending on usage, pay-as-you-go might be cheaper than a ChatGPT Plus subscription.
However, some of these points might improve in the future, so ultimately, it's just a hobby.
Repository
The repository is github.com/shuntaka9576/oax. The tool is named oax (OpenAI eXecutor).
Requirements
- OpenAI API key
- A synchronously executable editor like Vim/Neovim (I haven't tried it, but Nano probably works too)
Installation Method
You can install it with the following commands:
brew tap shuntaka9576/tap
brew install shuntaka9576/tap/oax
After installation, you'll need to set up your API key and preferred editor. This article omits those details. Please refer to the QuickStart in the README.md.
Supported Features
- Interactive chat function like chat.openai.com (
oax chat) - Profile function (allows requesting with a specified API key even if you have multiple accounts; can be specified with the
--profileoption)- Requests with Organization ID are also possible
See the README.md for more details.
Chat Function
Starting a New Chat
When you start a new chat, a chat history file in TOML format is created. The chat history file is saved in ~/.config/oax/chat-log.

The chat history file has the following format, where role="user" is the questioner and role="assistant" is the reply from ChatGPT. All messages arrays are sent to the OpenAI API, allowing for context-based chat.
[[messages]]
role = "user"
content = '''
Let's play Shiritori (word chain game).
'''
[[messages]]
role = "assistant"
content = '''
apple
'''
[[messages]]
role = "user"
content = '''
gorilla
'''
You can also change the path where chat history is saved. For example, if you use ghq, you can manage chat history under Git control by specifying ~/repos/github.com/{YOUR_OWNER}/chat-log. Here's how to change it:
oax config --settings
[setting]
editor = "nvim"
chatLogDir = "~/repos/github.com/shuntaka9576/chat-log" # <-- Add this line
For more details, please refer to here.
You can specify the model with the -m option. The default is gpt-3.5-turbo.
$ oax chat --help
Usage: oax chat
Provides a dialogue function like chat.openai.com.
Flags:
-h, --help Show context-sensitive help.
-p, --profile=STRING Specify the profile.
-v, --version Print the version.
-m, --model="gpt-3.5-turbo" Specify the ID of the model to use(gpt-4, gpt-4-0314, gpt-4-32k, gpt-4-32k-0314, gpt-3.5-turbo,
gpt-3.5-turbo-0301).
-f, --file=FILE Specify the chat history file with the full path.
Resuming a Chat
Specify the chat history file with its full path using the -f option.

Template Function
The chat function can be used more conveniently by editing the configuration file.
oax config --settings
[chat]
# Default model setting (option takes precedence if -m is used)
model = "gpt-3.5-turbo"
# Conversation template settings for starting a new chat
[[chat.templates]]
name = "friends"
[[chat.templates.messages]]
role = "system"
content = "You are ChatGPT, a large language model trained by OpenAI. You are a friendly assistant that can provide help, advice, and engage in casual conversations."
Execute with the template name specified using the -t command, as shown below:
oax chat -t friends
The content of the specified template will be inserted into the chat history file.
[[messages]] # <-- Inserted
role = "system"
content = '''
You are ChatGPT, a large language model trained by OpenAI. You are a friendly assistant that can provide help, advice, and engage in casual conversations.
'''
[[messages]]
role = "user"
content = '''
# Remove this comment and specify content to send to OpenAI API; otherwise, nothing is sent.
'''
Technology
Go
- Easy to distribute as a single binary
- Easy to achieve multi-threading using channels
- Powerful binary release distribution features of goreleaser (cross-compilation, brew tap integration, etc.)
In this case, it subscribes to SSE events mentioned later, sends them to a channel when acquired to update the differences, thereby achieving a display similar to the web version of ChatGPT.
It writes to bytes.Buffer and standard output using io.MultiWriter, and outputs to a TOML file when a response is returned.
My Go experience is limited, so the code is quite challenging, but I intend to refactor it in the future.
TOML
TOML was adopted for two types of configuration files and the chat history file. This was chosen because enclosing text with ''' avoids the need for escape sequences for newlines, improving readability. However, when deserializing and writing ChatGPT's response content to TOML, it doesn't become ''' notation, so the strings are concatenated in a loop for output.
OpenAI API
OpenAI's API /v1/chat/completions supports SSE (Server-Sent Events).
I struggled a bit because the Go libraries for handling SSE events didn't support POST SSE. For details, please refer to Trying out ChatGPT's Streaming (SSE) API (Go implementation). I'm referencing an SSE library forked from Feature Request: Support non-GET method and request body in the sse client as a git submodule.
By using the replace directive in go.mod, you can avoid rewriting the import path to your GitHub Owner name.
When using git submodules, you need to set the submodules option of actions/checkout@v3 to true.
Consideration and Postponement of bubbletea
I had planned to create a UI similar to bubbles' example using bubbles' textarea and viewport, but I had to postpone it due to lack of skill.
- viewport
- Lines with long widths do not wrap automatically.
- Automatic scrolling needs to be implemented separately.
- Standard output is handled by bubbletea, so for this requirement, if I wanted to do normal standard output, I would need a separate implementation.
I want to study more and master it.
Future Plans
I am personally satisfied with the current use case, but I would like to address the following in the future:
-
Externalize the default model setting to the configuration fileSupported in v0.1.0 - Summarize conversation history content from chat.openai.com-like history to create a title and save it as the chat history filename.
-
Template functionSupported in v0.1.0 - Windows support (currently in progress...)
- Introduction of other useful OpenAI API features.
I have a feeling that something much more amazing will be released officially, but I intend to differentiate and improve this tool as a lightweight and easy-to-use option...
Discussion