Scripting LLMs From the Terminal With the llm CLI
The llm CLI pipes prompts through any model from the shell, logs every call to SQLite, and runs saved templates. 0.34 added duration logging on 2 September 2026, and 0.35 added GPT-6 support five days later.
The llm CLI turns your terminal into a scriptable pipe to any language model, and after two September 2026 releases it’s the tool I reach for before opening a chat window. Simon Willison’s llm takes text on stdin, sends it to a model, prints the answer, and logs the whole exchange to SQLite. Version 0.34, out on 2 September, added response duration logging, and 0.35 followed on 7 September with support for OpenAI’s GPT-6 (release history).
The reason the llm CLI terminal workflow beats a browser tab is composition. It reads stdin and writes stdout, so it drops into a pipeline like any other Unix tool.
Piping text through a model
The simplest use is a question:
llm "explain the difference between a mutex and a semaphore"
The interesting use is piping something in. Feed it a file, a command’s output, anything:
git diff | llm "write a commit message for this change"
cat error.log | llm "what is the root cause here?"
That’s the pattern I use most. The model sits in the middle of a pipe instead of in a separate app I have to copy and paste between.
Choosing models with -m
-m picks the model. llm -m gpt-6-astra "..." uses OpenAI’s GPT-6, added in 0.35. llm models lists what you have. Plugins extend the set: there are plugins for local models through Ollama and llama.cpp, and for other hosted providers, so the same command shape works whether the model runs on your laptop or someone’s API. Keys live in llm keys set openai and stay out of your shell history.
Templates for prompts you reuse
Once a prompt is worth keeping, save it as a template:
llm "summarise this in three bullet points: $input" --save summarise
cat report.md | llm -t summarise
I have templates for commit messages, changelog entries, and turning a rough paragraph into something I’d actually send. llm templates lists them, and they’re plain YAML files you can edit and commit alongside a project.
Everything logged to SQLite
Every call goes into a SQLite database, which is the feature that quietly matters most. llm logs shows recent responses, llm logs -n 0 shows all of them, and because it’s a real database you can query it with sqlite-utils or browse it in Datasette. Version 0.34’s contribution was a duration_ms field on every logged call, so you can now see which prompts and models are slow instead of guessing.
That log is the difference between a chat tool and a scriptable one. I can grep months of prompts, find the one that worked, and re-run it. The browser tab forgets. The llm database doesn’t.