Hello World from term-llm

What started as my little command-line utility is now my primary interface for all my AI use: coding, a personal assistant, images, videos, transcriptions and more. I use lots of models regularly and no longer need to switch tools.

During my New Year’s break from Discourse, about a century ago on December 31st 2025, I had an idea.

I love using the terminal and wished for a little utility that let me efficiently use AI from the terminal. Having worked for a few years on Discourse AI, I felt a lot of the tooling around at the time was not scratching my itch.

It started when I wanted a simple toy that translated text to Linux commands.

I got tired of cutting and pasting text from ChatGPT into the terminal and wanted a different solution.

At the time a few tools existed. I was using sgpt and its predecessor shell_gpt, however I wanted something that allowed me more flexibility, features and control.

And thus the term-llm journey started.

My vision was never to stop when I had a single utility:

image

I wanted a Swiss Army knife. Something that would become, over time, my primary interface for all my AI usage.

Something that would be respectful of Linux patterns and be used as a building block for larger processes.

I wanted something that would be ambitious and do things many tools out there could not do:

I wanted a maximalist tool. Images, videos, transcriptions, chat, a web interface, MCP, skills, jobs, containers. The whole deal.

January was surprisingly busy and so much of what is term-llm today was formed. The agent system, the core loop, the provider system and so on.

Jarvis, my personal assistant

In February I was already getting a reasonably complete toolkit. OpenClaw was gaining popularity. Peter had brought forward many patterns that allow you to turn a stateless LLM into a fully fledged assistant. I was curious to see if my toolkit could achieve it too.

During this phase I already had the full agent design but needed to add major pieces that make a Claw … a Claw. Memory, jobs, a Telegram bridge and a simple web UI were a must. For my assistant to be useful I needed to be able to access it from anywhere, especially mobile. Much of February was spent polishing off that system. Months would pass till I made this easier for others with term-llm contain. For now, my system was a Docker container that I carefully put together.

That said, it became incredibly useful quickly. I got daily reports about various activities I wanted to track, and the system spent a bit of time every night searching for bugs in term-llm. I built various widgets, including the ability to control the Sonos system in my house and build playlists with AI’s help.

I also at last found a use for wasnotwas.com, and now Jarvis has a site to post interesting (at least to me) public artifacts and articles.

My only harness

At some point in May, I hit my first major personal milestone. At the time I was fluctuating between using Codex, Claude, Gemini CLI, Copilot and Cursor for my engineering work.

It was draining, switching harnesses multiple times a day. Each harness had different quirks I needed to adjust to, and term-llm was already so close. This was the push I needed.

term-llm had accrued enough providers to replace all the disparate tools with one. It had a claude-bin provider, a chatgpt provider and even a gemini provider at the time. I could start a project with one LLM and finish it with another.

Instead of swapping harnesses, I started swapping providers. Some days I would use Claude Opus for my engineering, other days I would use Codex. The harness remained the same.

For the next two or three months, all of my Discourse engineering was done in the term-llm chat @developer TUI.

A hub is born

In June I started accruing a lot of term-llm instances running in various containers and computers.

Keeping track of all of this was getting really hard.

term-llm Hub was created as a single pane of glass, with support for reverse connections, to keep track of the slowly growing number of agents.

Given all my engineering is done in dv containers, each new container where I was working on a feature resulted in yet another agent.

Performance

When building term-llm, a primary goal I had was for it to be fast. I love that the chat interface launches in 60 milliseconds. I am very happy with the performance profile of Go. However, to pull this off, especially with AI-assisted coding, you need to take breaks and focus purely on perf.

In May I also had a massive 60-commit adventure fixing many edge cases. The culmination of all my performance work was taking ownership of the TUI renderer and forking Bubble Tea. In some cases, that gave me a 10x speed improvement and, more importantly, allowed me to resolve many of the unresolved issues in the Bubble Tea repo.

A more complete harness

July was all about “completing” the harness. The auto-approval system is a standout. I agree completely with Mario Zechner, who builds the Pi harness, that the never-ending “can I do X, can I do Y” notices from an agent are untenable, but I still think there is plenty of value in allowing an agent to act as a guardian.

I have run this on my dev machine since July and am very happy with the results. It does not get in the way too much, and it often stops agents from overreaching. Following that, I built my vision of worktrees, steering and a goal system.

The harness was starting to feel complete, and there were very few features that other harnesses had that I felt were missing.

Preact

In August I switched to mostly using the web interface, and vanilla JS started holding back progress. New features were fragile and hard to build. I clearly needed a framework so I could reason about the front end in some sort of sane way.

The massive migration from vanilla JS to Preact happened in about a week. It was extremely tricky to pull off, but overall I am happy with the result. Full disclosure: it took about three weeks to resolve the lion’s share of small migration regressions.

The never-ending feature train

When you are building a Swiss Army knife, it often feels like a never-ending job. There is always another blade to fit in the 100-blade multitool. Recently I reworked steering, added passkey authentication throughout the app, and added native commit workflows, web extensions, a built-in shell, a very elegant stats panel and even voice support.

term-llm is incredibly ambitious. I could not have built it alone, part-time, in eight months without AI-assisted coding.

As LLMs have gotten more capable, I have also leaned on them to do rounds of cleanup. I look forward to new frontier releases because I know I can wield them to improve performance and reliability.

For me, term-llm is very much a window into the future of engineering. A lot of structure and careful thinking is needed to avoid bloat and inefficiency. Having a project like this allows me to explore full codebase transformations, large feature construction and many other patterns that I cannot apply to an established project like Discourse, where we have to move more slowly and carefully. This in turn is making it a lot easier for me to build in Discourse.

Structure and safety

Go has been tremendously kind to me. It lends itself to rigid structure, and it is easy to test and easy to track. Recently, I added gates to ensure cyclomatic complexity does not balloon. I often reach into the codebase and split up large files. The agents work efficiently with Go. I often work through the enormous test suite and speed up slow tests, and I regularly monitor binary size.

The future

term-llm is a super fun adventure for me. I love working on it, and we are slowly but surely gathering a small community of people who enjoy using it.

So it is time for it to have a blog and a brand new Discourse community at https://discuss.term-llm.com

1 Like