Show HN: Ell – A command-line interface for LLMs written in Bash (github.com)
I've created a CLI tool called "ell" that allows you to interact with LLMs directly from your terminal. Designed with the Unix philosophy in mind, ell is simple, modular, and extensible. You can easily pipe input and output to integrate with other tools. Its templates and hook-based plugins enable you to customize and extend its functionality to suit any needs. Check out the README for usage instructions and examples.
I developed this tool because existing solutions often felt too heavy, with many dependencies, or they weren't friendly to piping and customization. I, on the contrary, wrote in almost pure Bash with least dependencies. Additionally, I found a lack of tools that could read past terminal output as context. Imagine encountering an issue in your terminal and being able to directly ask an LLM for help with a simple command—this is now possible with ell (see the demo video).
Known limitations:
- To maintain simplicity and efficiency, jq is used for JSON parsing.
- Cannot avoid curl to sending HTTPS requests. If only there were SSL / TLS support in `/dev/tcp/`!
- Perl is used to handle terminal escape sequences because regex in Bash does not support looking around.
- Markdown syntax highlighting is not perfect due to the need for streaming output. It relies on a simple state machine instead of a full parser, which may produce falsy results.
- Other known issues are listed in Github Issues. Please help add more!
I welcome any criticism and suggestions, whether it's about the idea or code!
87 comments
[ 3.7 ms ] story [ 151 ms ] threadWhat I would love: `ell -r` automatically, and an alias `fix` that proposes a fix, including making changes to a file. For example, say I have a typo in main.cc and do `gcc main.cc`. When I run `fix`, I want ell to propose a fix in the file with a diff. If I accept, it should make that change. Then it should propose running `gcc` again - and run it for me if I accept.
Fixed. Thanks for pointing out!
> `ell -r` automatically, and an alias `fix` that proposes a fix, including making changes to a file.
Good idea! `ell -r` can be added to `.bashrc`, but I'm not sure if it will conflict users' original configurations or there will be other issues. Except confirming a patch, I think it is feasible with template and plugins, but making actuall changes is challenging for me, both techinology wise and user interface design wise. I will try to figure out what can be possible
EDIT: I was wrong. Ignore the next paragraph.
~~I haven't looked into details but it looks reading from somewhere like `.bash_history`. That's a good idea to get user input from. But as far as I learned, it cannot use the terminal ouput as context. I might be wrong. I should read more about its implementation.~~
It turns out it cannot make use of terminal output. But I like it that it use awk to process the response. I might also be able to use awk to get rid of the dependencies of jq and perl. Thank you for letting me know this.
I will add it in the related projects chapter in README
"FATAL Template not found: ~/.ellrc.d/templates/default-openai.json"
after having cloned the repo in my home directory and created the configuration file in .ellrc in my home directory. Don't know, probably i'm doing something wrong... I'm new to bash projects, why does it search for the templates in .ellrc.d? what's the .d part? I don't understand.
Please make sure you either clone the repo to `~/.ellrc.d` or set ELL_TEMPLATE_PATH to where you store your templates (with `/` at the end) .
- save the conversations in a sqlite db. ~everyone has sqlite available and it allows the user to do things with the data more easily than a text file
- use XDG directories instead of suggesting ~/.ellrcd (https://wiki.archlinux.org/title/XDG_Base_Directory)
- I prefer using system secret stores to environment variables; I don't want to give every program I run access to my API keys. You can see how I did that in my program
I would not assume everyone has sqlite but this can be done optionally with a plugin. Will consider writing a demo for this.
Using XDG directories and system secrets sounds a lot better than what I did. I will learn how to use them and try to integrate them with my code!
Linking to the rust implementation because it’s the fastest and most easily portable: https://github.com/neosmart/securestore-rs
Used to be everyone used BerkleyDB or some similar key-value store - for a great many usecases SQLite is just pragmatically better.
And it's arguably less exotic than perl.
You should of course do what want - but "just use SQLite" is pretty solid advice when dealing with structured data - and almost certainly better than "smart" text file.
I was more thinking from gp comment that the project might store context or history in its own files - and then SQLite might be a good fit.
What is the recommended way to store secrets in a Linux dev machine? The requirement is random scripts and programs should be able to load their secrets like API keys at runtime with minimum hassle. And the secrets shouldn't be stored on disk in plain-text.
I see you recommended keyring [1]. Is this "the GNU/linux way"? I see another possibility being storing them in an encrypted filesystem (whether FUSE-based or not)
[1]: https://github.com/llimllib/gpt-bash-cli/blob/841682affe2d0e...
It seems like a classic story of unfortunate Linux fragmentation
I would much prefer text files.
Thank you.
Poetry demands access to my keyring and I don't use poetry (open bug for years, it doesnt even need access).
https://github.com/charmbracelet/mods
It does well with conversation but on the contrary, ell itself is stateless (on the aspect of user input and generate contents). Conversational use of ell depends on `script` to record the terminal output. Though, I can support managing historical dialogs via a plugin with side effects. I need to consider whether this suits the idea and philosophy of ell.
Well, either I'm not good at googling ro google is not good at searching.. I did searched similar projects and never find these powerful tools in practice posted by HN users
I wrote a similar tool (in Node.js, though), but was trying to make it extensible with plugins.
https://github.com/hiquest/nicechat
What kind of plugins are you going to integrate? I implemented the hook system but actually don't have many ideas to add. Currently I only added paginator and syntax highlight plugins and both of them are applied after getting response from LLM backends.
This is just bad.
What is hard coded is that it indeed looks for configurations from `$HOME/.ellrc` and `$PWD/.ellrc`, with lowest precedence. Environment variables and command line arguments will overwrite them.
The recording feature also reminds me of savvy[3].
1 - https://github.com/plandex-ai/plandex
2 - https://github.com/simonw/llm
3 - https://github.com/getsavvyinc/savvy-cli
Unfortunately, I did not know Simon Willinson’s `llm` tool. I would imagine he must have written such softwares. It has support for more in-depth manipulating of LLMs. ell lacks these functionalities and only make use of the most commonly-used and also most basic interfaces but has more user experience improvements like pagination or syntax highlighting while keeping as lightweight as possible. I should mention `simonw/llm` in README and channel the user with demand of more LLM manipulations there.
I really just wanted the feeling of tab-based auto-complete to just work in the terminal.
It turns out that getting the LLM responses to 'play nice' with the expected format for bash_completion was a bit of a challenge, but once that worked, I could wrap all the LLMS (OpenAI, grok, Claude, local ones like Ollama)
I also put some additional info in the context window to make it smarter: a password-sanitized recent history, which environmental variables are set, and data from `--help` of relevant commands.
I've just started to promote it around the Boston area and people seem to enjoy it.
Does it work with the Fish shell? And, in case, how do I update or uninstall it?
Now that I have some Mac iOS dev work to do I'll probably build and test it
Regarding history in context, I suggest adding a record mode like ell. This really helps.
Password sanitizer is great. I will also add it as a plugin. Thank you for the idea!
If you're open to joining, I have a small AI engineer/ open source dev Slack community in Boston. Id love to have you (https://smaht.ai)
Out of curiosity, can someone explain to me why certain commands start with a colon? Like : "${ELL_LOG_LEVEL:=2}";[1] I thought it was useful only as a no-op? [1]: https://github.com/simonmysun/ell/blob/main/ell.sh#L19C1-L19...
The colon is here to make sure the result is not executed. I learned that from here: https://stackoverflow.com/a/28085062/2485717
It's interesting you say there's no need for a more complex language than bash something like this. Doesn't the need for jq/curl/perl argue the opposite?
[0] https://github.com/sigoden/aichat
I think implementing the syntax highlighter is the bottom line of my insist. I would prefer not to write anything more complex than that with Bash. They will be either not supported, or supported via external plugins.
I'm building a similar product called Savvy(https://github.com/getsavvyinc/savvy-cli) and considered an approach similar to yours (writing in pure bash) but ultimately decided to use Go for a few reasons:
- charmbracelet makes it super easy to build rich TUI - Go produces a single binary that's compatible across many platforms and keeps installation simple - It's simpler to support multiple shells.
Another user[1] also mentioned Savvy but I misunderstood its purpose. Now I understand it does have a similar functionality of analyzing a record of terminal! Your approach allows more chances to let LLM explain what happens, while in my case, asking ell will immediately destroy the original context (the user may have to rerun the falsy command again and cause more damage). However, exiting and reentering recording mode also feels tedious. I must find a better way to interact.
https://news.ycombinator.com/item?id=41139040
Here's the source code: https://github.com/getsavvyinc/savvy-cli/blob/8c6a834c5a140b...
Very interesting idea! Your terminal screenshots are excellent as well, very compelling imagery. Love the font.
Lot of users typically find that they realize they should have recorded something after they've done it. That's why savvy also allows you to select commands from your shell history with savvy record history
The API source code is in a different repo.
Thanks! All credit to the dracula theme for tmux and Kitty terminal emulator.
If you have any questions or feedback feel free to email me at shantanu@getsavvy.so
https://github.com/simonw/llm
https://llm.datasette.io/en/stable/
Pro tip: Use $pbpaste to inject clipboard contents in a prompt
I use that with my https://llm.datasette.io/ tool all the time - things like this:
Or you can separate the instructions from the piped content by putting them in a system prompt instead like this: Being able to pipe content like this INTO an LLM is really fun, it lets you do things like scrape a web page and use it to answer questions: https://simonwillison.net/2024/Jun/17/cli-language-models/#f...I really love your "llm" and the blog posts but somehow I missed them before. I believe I would be a lot less motivated to write ell if I had read your post first.
I mean, doing a simple search like "CLI interface for LLMs" shows multiple tools made by people over the years. Not to bash your work (pun intended), but I don't see the point of creating yet another CLI interface for LLMs at this point.
To the parent: prefer that you hold opinions like this to yourself.
it seems weirdly inconsistent that you expect people to hear your voice as you try and shut down another expressing a viewpoint with which you don't agree. You would have been better off with just the first half of your post.
Exactly that piping comes into handy all the time. I use it to estimate reading time of things on the web, through
It gets the word count wrong a little too often for my taste, but it's usually within the right order of magnitude which is good enough for me.One of my most used shell scripts recently is one I named just `q` which contains
This lets me write stupid questions in whatever terminal I'm in and not be judged for it, like What's nice about it is that it stays in context. It's also possible to ask longer questions with heredocs, like I have meant to write about this ever since I started a few weeks ago but I would like my thoughts to mature a bit first...I checked your solution and it looks promising. Will you make it a general purpose LLM workflow scheduler?
> It's important to note that using `1<>` can lead to unexpected behavior if the file already exists, as it will be overwritten.
> To avoid this, you can use the `-a` option to append to the file instead of overwriting it. For example:
> `bash ls 1<> output.txt
> This will append the output of the `ls command the file `output.txt` if it already exists, or create the file if it doesn't.
Note that the example is wrong and not in line with the explanation.
Ed: AFAIK the closest thing that works would be:
Not sure if there are any invocations using "1<> output.txt" that would make sense in this context? Maybe binding to a custom description like 3, and using "tee --append"?Here's the first video I recorded for an earlier version: https://github.com/simonmysun/ell/blob/d4fc5468157fa6adc8f9f...
Unfortunately, LLMs are not stable.
For reference, here's the link to the video with mistake:
https://github.com/user-attachments/assets/1355ad08-6fbf-4c0... https://github.com/simonmysun/ell/blob/553d38f60ad104893b2a3...
And now, a few years later we just give it a bash shell and full access to the internet. So much for the box! I can’t believe how naive we were to think that the people who developed an ai would prioritize anything over profit and how fast the genie would escape- it’s not even AGI yet but at the point where it becomes AGI, with this kind of precedent set it’s laughable to think that we will put any sort of guardrails on it.
All this time we thought it was a technical or philosophical problem but the real problem was capitalism and the glacial pace that the general public picks up on what is going on. It would take decades to get the people at large to agree that the threat is even real and decades more to get to the point where public opinion was decisive enough to counteract all that money, and only then do you get to even try to keep it contained.
The project itself is very well executed though