What's on my radar
What I’m building
mcp servers
What I’m maintaining or improving
lmslinkconvotranscribewebshotfind-panesagentdevcontainerwhisper
Details
lm
lm is a Go CLI tool for calling LLMs from the command line. I wrote this a couple years ago as an excuse to learn some Go. At the time, I thought Go was the perfect language for dealing with LLMs because it makes writing concurrent code really easy. In retrospect python might have been a better language, though maintaining lm helps keep my Go skills from rusting.
This tool is in most respects a strictly inferior version of Simon Willison’s llm tool, which has the same core functionality coupled with a bunch of other useful features. That said, there are some aspects of lm that I like (llm likely supports these as well, I just haven’t taken the time to learn)
lmis multimodal - it works with both text and images. Images can be generated in a bunch of ways:--imageFilesis the option I normally use for passing in images, but image URLs (via--imageURLs) and even screenshots (taken via--screenshot) are supported as welllmis a small tool and relatively easy to understandlmhas a--cachefeature which caches recent responses. For instance, if you run
echo "hello world" | lm --cache
echo "hello world" | lm --cache
the first response will call a LLM, but the second will use the cached response generated by the first call. This is convenient for scripts
I use lm extremely heavily. A bunch of the projects below (convo, transcribe, find-pane) are just tiny wrappers around lm
slink
slink is a bash script that I use for symlinking scripts into my PATH. slink is mostly a thin wrapper around ln -s; nonetheless I find it extremely useful
convo
convo is an extremely small, extremely powerful bash script which allows you to have conversations with tmux panes.
An example is worth a thousand words:
$ mkdir /a/b/c
mkdir: cannot create directory ‘/a/b/c’: No such file or directory
$ convo "what am i doing wrong here?"
The error message you're seeing, `mkdir: cannot create directory ‘/a/b/c’: No such file or directory`, indicates that the parent directories (`/a` and `/a/b`) do not exist, so `mkdir` is unable to create the full path starting from `/a`.
To fix this issue, you can use the `-p` option with `mkdir`, which tells `mkdir` to create the parent directories as needed. Here’s how you can modify your command:
mkdir -p /a/b/c
This command will create the entire directory path `/a/b/c`, making any intermediate directories (`/a`, `/a/b`) if they do not already exist.
Effectively all convo is doing is taking the last N lines (default 1000 but you can change via -l <num>) of your pane and sending it into lm. Something spicier I’ve been working on is piping this output to agent instead, which would allow convo to strike up an agent that can interactively work in your current pane. But this is highly experimental and obviously pretty dangerous.
transcribe
transcribe is a tiny bash script that transcribes screenshots into text. It only works on MacOS. I use transcribe regularly at work to do things like extract text from a slack screenshot.
webshot
webshot is a python script for taking screenshots of websites. It’s a really thin wrapper around puppeteer. I use it to generate screenshots for lm
find-panes
find-panes is a bash script that allows you to, effectively, grab output from other panes in your current tmux window. It’s like grep but on tmux panes. For instance, if I have a pane that’s running jekyll and hitting some errors, I could run find-panes to show what the error is
[arch@archlinux shell-scripts]$ find-panes "the one with jekyll errors"
****************
╵
/home/arch/scripts/pages/style.scss 5:9 root stylesheet
Deprecation Warning [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
More info and automated migrator: https://sass-lang.com/d/import
╷
6 │ @import "variables";
│ ^^^^^^^^^^^
╵
/home/arch/scripts/pages/style.scss 6:9 root stylesheet
Deprecation Warning [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
More info and automated migrator: https://sass-lang.com/d/import
╷
285 │ @import "highlights";
│ ^^^^^^^^^^^^
╵
/home/arch/scripts/pages/style.scss 285:9 root stylesheet
Deprecation Warning [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
More info and automated migrator: https://sass-lang.com/d/import
╷
286 │ @import "svg-icons";
│ ^^^^^^^^^^^
╵
/home/arch/scripts/pages/style.scss 286:9 root stylesheet
...done in 0.077033472 seconds.
q^[
****************
I mostly use this in prompts to lm, in the event that I have to refer to output from other terminal windows.
agent
agent is a work in progress python library for building agents using the OpenAI Agents SDK and MCP. The idea is that you can define various MCP servers for file search, web browsing, etc. and make these available to an agent to use.
The current setup I have works but it isn’t super elegant. I need to break out my custom servers into another repo where they can be easily installed and managed using uvx. I also need to experiment with other agents frameworks since I don’t want to be tied to OpenAI long term. smolagents looks promising
devcontainer
devcontainer is a docker container that replicates, as closely as possible, my local development environment. I started the project to see how far I could go in making a container I can use for local dev. Obviously this container, is very, very opinionated. One of the aspects of the container that I like is that it pulls in my dotfiles, shell scripts, and lm tool from github. This way any updates I make to my dotfiles, shell-scripts, or lm repo are all reflected in the container.
whisper
whisper is a shell script that allows you to share local files with remote servers, docker containers, etc.
I’m really proud of this script, even though I don’t use it a lot. The novelty is that whisper doesn’t actually share the files over the network; it actually embeds the files as part of the entrypoint to whatever you are calling out to. It does this by dynamically generating an unpack function which contains a HEREDOC with the base64-encoded tarball of whatever you want to share baked in. The function proceeds to bsae64 decode and untar the result.
It’s easier to just see it in action:
[arch@archlinux tmp]$ mkdir /tmp/test && cd /tmp/test
[arch@archlinux test]$ touch a b c
[arch@archlinux test]$ /home/arch/scripts/shell-scripts/whisper.sh
function unpack() {
cat <<'EOF' | base64 -d | tar -xzf -
H4sIAAAAAAAAA+3STQoCMQyG4RylJ2hTTdrz1Nm4HvX+dhaCK38GIgjvswm0hS/wNRcJp1N332bt
rs/zQaqrerdarc3z3o4HSR6/msjtch1rSjLW5fzq3bv7P5XLCM/YCm5m3/Q/P4AkDd9M6L+cwjN2
9W/0/wu5LOEZu/p3+gcAAAAAAAAAAAAAAPjEHRYZhrQAKAAA
EOF
}
main ()
{
unpack;
/bin/bash
}
main
[arch@archlinux test]$ docker run --rm -it --name testtest nginx /bin/bash -c "$(/home/arch/scripts/shell-scripts/whisper.sh)"
root@d3da9c81f659:/# ls
a b bin boot c dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
Something interesting I learned when developing this script is that bash commands have a limit length! This length is usually defined by ARG_MAX, and can vary from system to system. The implication is that there is an upper limit to the amount of data you can send through whisper (so you wouldn’t be able to use it to send large files, but you could use it to send a shell script that downloads the large file for you).
gash
gash is a shell script that lets you call the OpenAI API via cURL. The script is fairly limited, and has a subset of lm’s functionality.
The main reason I wrote gash was to make it easier to call LLMs from other remote servers/containers/etc. All gash needs to run is curl, bash, and jq which many remote systems have. gash offers an --export option that will actually echo out its own code and whatever OPENAI_API_KEY is currently set in your environment. You can feed this into an entrypoint to effectively get gash set up in remote environments without having to download any external tools.
[arch@archlinux gash]$ docker run -it --rm ricsanfre/docker-curl-jq bash -c "$(./gash.sh --export); export -f llm; bash"
bash-5.1# echo "tell me a fun fact" | llm
Sure! Did you know that honey never spoils? Archaeologists have found pots of honey in ancient Egyptian tombs that are over 3,000 years old and still perfectly edible! Honey's low moisture content and acidic pH create an environment that resists bacteria and spoilage, making it one of the longest-lasting foods on the planet.
uvx mcp servers
I want to make personal MCP servers easy to run via uvx via this trick. I will update this once the project is more fleshed out :)
