10 ms·
Show HN: Ell – A command-line interface for LLMs written in Bash
Hi HN!
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!
- mFixman 2y agoThe program seems to assume you'll clone it in your home directory, and has paths hardcoded to `~/.ellrc.d/`. This is just bad.
- simonmysun 2y agoI wouldn't say these paths are hardcoded. They are just default values. You can set the variables manually. 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.
- SoftTalker 2y agoConvention over configuration isn't bad per se, as the alternatives tend to devolve to bikeshedding.
- piyushtechsavy 2y agoSounds cool.
- simonmysun 2y agoThx!
- danenania 2y agoThis is cool! Using pure bash and unix tools is an interesting approach. I built Plandex[1] which has some similar goals (no dependencies, terminal-based, supports piping into context) but it takes quite a different route to get there—I’m using Go and compiling static binaries. It’s also ‘higher level’ and specifically focused on coding, whereas ell seems like a very lightweight and general purpose LLM tool. It reminds me a lot of Simon Willinson’s `llm` tool[2]. Are you familiar with it? The recording feature also reminds me of savvy[3]. 1 - https://github.com/plandex-ai/plandex https://github.com/plandex-ai/plandex 2 - https://github.com/simonw/llm https://github.com/simonw/llm 3 - https://github.com/getsavvyinc/savvy-cli https://github.com/getsavvyinc/savvy-cli
- simonmysun 2y agoThanks! Plandex is also nice! I never thought of such workflow. 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.
- threecheese 2y agoThat’s quite an honest and emotionally mature response, and I am always glad to find Real People(tm) around the internet. Rare these days. Your product looks great btw! Consider me another Stargazer, and keep building it.
- simonmysun 2y agoThank you!
- skruzel 2y agoI also have a similar tool called https://autocomplete.sh https://autocomplete.sh https://github.com/closedloop-technologies/autocomplete-sh https://github.com/closedloop-technologies/autocomplete-sh 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.
- stared 2y agoLooks interesting! Does it work with the Fish shell? And, in case, how do I update or uninstall it?
- skruzel 2y ago`autocomplete remove` will delete it. I haven't tested it in fish / zsh shells. Now that I have some Mac iOS dev work to do I'll probably build and test it
- simonmysun 2y agoWow that's very useful! I have also thought of completion but my idea was more like copilot. The user experience of your script should be better. I'm glad I didn't start to write that. 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!
- skruzel 2y agoThanks for checking it out and the record mode is a great idea. I've been playing around with ways to get the terminal outputs but so far I haven't loved the UX of my solutions. Your co-pilot approach that can explain the commands and iterate is really valuable. 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 https://smaht.ai)
- alkh 2y agoLooks great! I work on a number of different machines, so having something lightweight(like written in shell) is always desired. 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-L19C25 https://github.com/simonmysun/ell/blob/main/ell.sh#L19C1-L19...
- simonmysun 2y agoThanks! The colon is here to make sure the result is not executed. I learned that from here: https://stackoverflow.com/a/28085062/2485717 https://stackoverflow.com/a/28085062/2485717
- pseufaux 2y agoThe : basically just tells bash to do nothing with the result of the line. So `: "${ELL_LOG_LEVEL:=2}";` would initialize `ELL_LOG_LEVEL` to 2 if it's not already set without producing any output.
- teamspirit 2y agoWill check it out. Personally been using aichat[0] for this. 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 https://github.com/sigoden/aichat
- simonmysun 2y agoIndeed. That's why I list them as limitations. My original idea was to get everything done with Bash. This is however not feasible as the reasons listed. Maybe I can get rid of jq and perl using awk, but that would sacrifice a lot of simplicity and readablity of the code. 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.
- Lord_Zero 2y agoSecond aichat. Super good. For Linux I created a little bash script that downloads the latest binary and unzips it into /home/me/bin
- joshi4 2y agoEll is really cool! I'm building a similar product called Savvy(https://github.com/getsavvyinc/savvy-cli 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.
- simonmysun 2y agoThanks! 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 https://news.ycombinator.com/item?id=41139040
- joshi4 2y agoYou're right, to counteract the friction I also allow users to create runbooks from their shell history. Here's the source code: https://github.com/getsavvyinc/savvy-cli/blob/8c6a834c5a140b83cc77c788e40738f510af7c12/cmd/history.go#L42 https://github.com/getsavvyinc/savvy-cli/blob/8c6a834c5a140b...
- threecheese 2y agoJust checked out Savvy; is the runbook-generating code (‘savvy record’) also in that repository? The one hosted at api.getsavvy. Very interesting idea! Your terminal screenshots are excellent as well, very compelling imagery. Love the font.
- joshi4 2y agoHere's the code for savvy record: https://github.com/getsavvyinc/savvy-cli/blob/8c6a834c5a140b83cc77c788e40738f510af7c12/cmd/record.go#L48 https://github.com/getsavvyinc/savvy-cli/blob/8c6a834c5a140b... 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
- raajg 2y agoBeen using the LLM cli by simonw and love it. https://github.com/simonw/llm https://github.com/simonw/llm https://llm.datasette.io/en/stable/ https://llm.datasette.io/en/stable/ Pro tip: Use $pbpaste to inject clipboard contents in a prompt
- simonmysun 2y agoI don't have pbcopy and pbpaste on my machine but injecting clipboard sounds interesting.
- simonw 2y agoDoes ell have the ability to pipe things INTO the tool? I use that with my https://llm.datasette.io/ https://llm.datasette.io/ tool all the time - things like this: cat somecode.py | llm -m claude-3.5-sonnet "Explain this code" Or you can separate the instructions from the piped content by putting them in a system prompt instead like this: cat somecode.py | llm -m claude-3.5-sonnet --system "Explain this code" 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/#frame_003914.jpg https://simonwillison.net/2024/Jun/17/cli-language-models/#f...
- simonmysun 2y agoDefinitely! For example, cat somecode.py | ell -f - If you prefer adding another piece of prompt instantly instead of adding it in the template: (cat somecode.py; echo "Explain this code") | ell -f - I should've added this into README. 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.
- behnamoh 2y ago> 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.
- edmundsauto 2y agoTo the creator, ignore this person. Thank you for sharing! To the parent: prefer that you hold opinions like this to yourself.
- respondo2134 2y ago
- heyrikin 2y agoThis is really cool.
- simonmysun 2y agoThx!
- deleted 2y ago[deleted]
- koinedad 2y agoI love the idea of piping my error messages into an LLM to help me debug. Would also love a solid local only LLM and have this basically in offline mode as well
- simonmysun 2y agoPlease stay tuned. Support for more providers are on the way!
- woolion 2y agoSo, last weekend I wanted to use an LLM to review some documents, and the problem I had was not so much the interface but that it's necessary to have some workflow management to re-run failed jobs and run the aggregate job once its dependencies are done. I ended up writing my own to do it, but I wondered if there are off-the-shelf solutions that already provide these kinds of work.
- simonmysun 2y agoI don't have a solution yet but it's also a problem I am trying to address. The output of LLM is not stable and robust and currently we can only adjust the prompt to improve it. Fundamental tools like piping in the shells cannot easily handle this. You must either rerun the whole pipeline or start to write more complex script which includes validating and parsing the output. I checked your solution and it looks promising. Will you make it a general purpose LLM workflow scheduler?
- woolion 2y agoThank you for your reply, that is good to know that (especially coming from someone with great bash skills). My intent was to have the simplest way to solve my problem and not any more. If I was going to make it more robust I would probably switch to Airflow, Luigi has nice features to make things simple but more limitations. I think there's Flowise that fits the bill for LLM workflow management, but I haven't had the time to investigate yet.
- simonmysun 2y agoThx! Will look into that.
- e12e 2y agoInteresting - unfortunately displays typical llm issues in the demo video: > 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: ls >> output.txt 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"?
- simonmysun 2y agoYou are right. I will replace the video. Actually the last time I record it wasn't this bad. And the script was kept and I didn't make much change when recording it again. Here's the first video I recorded for an earlier version: https://github.com/simonmysun/ell/blob/d4fc5468157fa6adc8f9f785c1c979238703d007/docs/demo.gif 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-4c0b-9ba6-2542fcb39acf https://github.com/user-attachments/assets/1355ad08-6fbf-4c0... https://github.com/simonmysun/ell/blob/553d38f60ad104893b2a32c160dd72f37f760c03/docs/demo-basic.webm https://github.com/simonmysun/ell/blob/553d38f60ad104893b2a3...
- fallingfrog 2y agoI love how a few years ago everyone was fretting over how to keep an AGI in a leakproof box, http://cecs.louisville.edu/ry/LeakproofingtheSingularity.pdf http://cecs.louisville.edu/ry/LeakproofingtheSingularity.pdf 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
- simonmysun 2y agoThat truly is a visionary article; thank you for sharing it. We still cannot afford to be complacent, but I believe it is the responsibility of the users to ensure safe and ethical usage.
- deleted 2y ago[deleted]
- bagas31 2y ago[dead]
- mherrmann 2y agoCool. The link to "Risks" in the README is broken. What 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.
- Zambyte 2y agoRegarding running ell -r automatically, you can just add it to your .bashrc
- mherrmann 2y agoYup. But the rest of the functionality is missing, I think.
- simonmysun 2y ago> The link to "Risks" in the README is broken. 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
- hacker-l 2y agoHi, they're also trying to do something similar with shell. I'm not sure who's better. [demo](https://x-cmd.com/mod/gemini https://x-cmd.com/mod/gemini) [source code](https://github.com/x-cmd/x-cmd/blob/main/mod/gemini/lib/main https://github.com/x-cmd/x-cmd/blob/main/mod/gemini/lib/main)
- simonmysun 2y agoCool! This is looks a lot fancier. 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
- curry798 2y agoIt looks beautiful and has many features, why are there so few star?
- simonmysun 2y agoI also wonder. It didn't appear in my search because, I guess, it has too many features and the feature I want to search has a relatively low weight. I also searched x-cmd on HN but there aren't many positive comments... I would expect it's more popular on HN because it's written in POSIX shell and awk.
- mvavassori 2y agoI don't know why i keep getting the error: "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.
- simonmysun 2y agoOh sorry that's my bad. The target clone path did not match the default value of template path. 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) .
- mvavassori 2y agoThank you, i always assume there's some magic part that goes on behind the scenes which i don't understand, especially in things i'm not familiar with... In fact it was just a path mismatch as the error suggested.
- simonmysun 2y agoYou are welcome. Please feel free to fire any issues you may encounter.
- llimllib 2y agoI wrote a similar tool I'm no longer maintaining: https://github.com/llimllib/gpt-bash-cli/ https://github.com/llimllib/gpt-bash-cli/ . Here are my suggestions: - 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 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
- ducktective 2y ago>I prefer using system secret stores to environment variables 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/841682affe2d0e33ecb8ab9c5a66765b25b8c134/gpt#L87 https://github.com/llimllib/gpt-bash-cli/blob/841682affe2d0e...
- llimllib 2y agoI did a fair amount of looking to try and support a Linux secret store! My conclusion was that I was too confused and so I punted to keyring which seemed to paper over a few different stores. It seems like a classic story of unfortunate Linux fragmentation
- amelius 2y agoI don't have sqlite, and I wouldn't know how to use it. I would much prefer text files. Thank you.
- fragmede 2y agoyou can visit the website https://www.sqlite.org/ https://www.sqlite.org/ where you can find copies of the program, along with instructions on how to use it. I would prefer to use a more advanced file format to hold records. Standing on the shoulders of giants, instead of their toes, as it were. Hopefully we can advance technology beyond the 1980's. Thank you for your understanding.
- trescenzi 2y agoHuge fan of Charmbracelet's mods. I've been using it for months now and it works great. Very customizable and the output is clean. https://github.com/charmbracelet/mods https://github.com/charmbracelet/mods
- simonmysun 2y agoThank you for letting me know! 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
- yanis_t 2y agoVery cool! I wrote a similar tool (in Node.js, though), but was trying to make it extensible with plugins. https://github.com/hiquest/nicechat https://github.com/hiquest/nicechat
- simonmysun 2y ago(Reading your comment and code reminds me that I might have confused user with the terms of the plugin I proposed and the plugin in popular LLM backends. I will make it clear in ell documents) 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.