- Go 100%
| |
screenshots.gitignoreCHANGELOG.mdconfig.sample.yamlgo.modgo.sumLICENSEmain.goREADME.md# lmcli - Large Model CLI
lmcli
is a general-purpose LLM harness. Use it to code, chat, or build custom agentic workflows!
We have screenshots.
Features #
- Buttery smooth at 1M+ tokens (sliding-window rendering)
- Composable, nesting Agents - Custom tools(exec-only, MCP coming soon) Sandboxtool execution- OpenAI-compatible and Anthropic-compatible API clients (talk to any local or remote API)
- Branching, persisted conversations (thanks, SQLite <3)
vi
-like keybindings- Export conversations to JSON or HTML
- Image support
- < 50MB of RAM per instance
Installation #
go install codeberg.org/mlow/lmcli@latest
Dependencies
lmcli
works best when the following tools are available:
- ripgrep - Grep tool
- libchafa - image rendering
- uv - Python tool
--with
dependencies - bubblewrap - Sandbox tools with
bwrap
Configuration #
See config.sample.yaml for the full example configuration.
Defaults
Defaults which apply to new lmcli chat
and lmcli new
sessions.
defaults:
model: deepseek-v4-flash
agent: default # default `lmcli chat` agent
codeAgent: coder # default `lmcli code` agent - just an alias
temperature: 1.0
maxTokens: 64000
effort: xhigh
Agents
An Agent is a system prompt and a set of available tools.
Each lmcli
session starts with a root agent, which the user interacts with
directly. This agent may then delegate tasks using the SubAgent
tool (provided it has been given access), and those sub-agents may delegate further.
Only synchronous sub-agents are supported at the moment; background/persisted sub-agents are planned for a future release.
The contents of AGENT.md
, AGENTS.md
, and CLAUDE.md
will automatically be
injected into the system prompt if any of them exist, and the agent has the
Read
tool.
agents:
- name: default
systemPrompt: You are a helpful assistant.
- name: coding
tools:
- Bash
- Glob
- Grep
- Read
- Write
- Edit
- Python
systemPrompt: |-
You are an expert software engineer...
(see config.sample.yaml for an effective prompt)
- name: web_summarizer
tools:
- WebReader
systemPrompt:
Your task is to summarize the web page.
Tools
The following tools are built-in:
Glob
- List files based on glob patterns
Grep
-ripgrep
-powered code searchRead
-
Read contents of a file
Write -
Write contents of a file
Edit -
Search and replace contents of a file
Bash -
Execute shell commands
Python
-uv run --with=<deps>
-powered python executionSubAgent
-
Spawn a sub-agent - seeAgents
WebReader -
Fetch web content using a Firecrawl endpoint.
Most tools execute out-of-process via the lmcli tool
sub-command, which may be wrapped in the Sandbox.
Tools usage is gated a very simple 'read-write-execute' permissions model.
Press ctrl-p
in the TUI to cycle through read-only, read-write, and read-write-execute
unattended permissions, or use lmcli chat|reply|new -p [rwx]
.
Custom tools exec-based tools are supported.
For example, given the GetWeather
example below, the model's call to
GetWeather(location="Tokyo, Japan", units="celsius")
would be piped the tool as:
{"location": "Tokyo, Japan", "units": "celsius"}
The executable's stdout is used as the tool result, which is returned to the model as-is.
Environment variables defined on custom tools get passed set on the environment during execution, hidden from the model.
MCP support is planned.
tools:
- name: WebReader
config:
backend: https://firecrawl.example.com/v1
apiKey: fc-...
- name: GetWeather
description: Get the current weather.
path: fetch-weather.sh
permission: read
parameters:
- name: location
type: string
required: true
- name: Read
- name: Write
- name: Edit
- name: Glob
- name: Grep
Providers
lmcli
supports local and remote OpenAI-compatible and Anthropic-compatible providers.
providers:
- name: deepseek
display: DeepSeek
kind: openai
baseUrl: https://api.deepseek.com/
models:
- deepseek-v4-pro
- name: anthropic
kind: anthropic
apiKey: ...
models:
- name: claude-sonnet-4-6
Sandbox
When enabled, lmcli
sandboxes local tool invocations with bubblewrap.
The sandbox is configured to prevent filesystem access outside of the CWD (with
configured exceptions), and to prevent leaking of lmcli
's environment (API keys, etc). Access to the host network stack remains a vector for escape.
Sandboxing is currently opt-in: sandbox.enabled
must be true.
sandbox:
enabled: true
remapWorkspace: true
bindDirs:
- /home/user/.config:/home/user/.config:ro
- /home/user/.cache:/home/user/.cache:ro
persistDirs:
- /tmp
Misc
Various settings that affect lmcli
's behavior.
conversations:
titleGeneration:
model: optional
effort: none
maxTokens: 1024
chroma:
style: onedark
formatter: terminal16m
tui:
hideHelpHint: false
Usage #
$ lmcli help
lmcli - Large Language Model CLI
Usage:
lmcli <command> [flags]
lmcli [command]
Available Commands:
chat Open the chat interface
clone Clone conversations
code Open the chat interface with a code agent
completion Generate the autocompletion script for the specified shell
edit Edit the last user reply in a conversation
export Export a conversation
help Help about any command
list List conversations
new Start a new conversation
prompt Do a one-shot prompt
rename Rename a conversation
reply Reply to a conversation
retry Retry the last user reply in a conversation
rm Remove conversations
view View messages in a conversation
Flags:
-h, --help help for lmcli
Use "lmcli [command] --help" for more information about a command.
Note: Use ctrl+h
in the TUI view for keybindings.
Examples
Start a new chat with the code
agent wired up to perform file editing.
$ lmcli code
Start a new conversation, imperative style (no tui):
$ lmcli new "Help me plan meals for the next week"
Send a one-shot prompt (no persistence):
$ lmcli prompt "What is the answer to life, the universe, and everything?"
tmux
Do you run lmcli
with tmux
? To get scroll-wheel scrolling working, you'll want the following in your ~/.tmux.conf
:
tmux_commands_with_legacy_scroll="lmcli"
bind-key -T root WheelUpPane \
if-shell -Ft= '#{?mouse_any_flag,1,#{pane_in_mode}}' \
'send -Mt=' \
'if-shell -t= "#{?alternate_on,true,false} || echo \"#{tmux_commands_with_legacy_scroll}\" | grep -q \"#{pane_current_command}\"" \
"send -t= Up" "copy-mode -et="'
bind-key -T root WheelDownPane \
if-shell -Ft = '#{?pane_in_mode,1,#{mouse_any_flag}}' \
'send -Mt=' \
'if-shell -t= "#{?alternate_on,true,false} || echo \"#{tmux_commands_with_legacy_scroll}\" | grep -q \"#{pane_current_command}\"" \
"send -t= Down" "send -Mt="'
This example will be removed when bubbletea
is able to handle scrolling in tmux on its own.
Roadmap #
I aim to keep lmcli
a light-weight and easy to understand shim between users and models. A driving philosophy is that models will grow capable of accomplishing increasingly complex tasks with access to the same set of simple tools.
In rough order of priority:
- MCP (local custom tools already supported)
- Skills
- Image output
- Built-in web search tool
- RAG-driven prior conversation search
- Computer use (connect to Wayland and/or X, pass display output to model, feed back mouse/KB inputs)
- Semantic conversation search
- Conversation categorization/tagging
- Token accounting
- Fleshed-out permissions model (beyond current read-write-execute model)
License #
MIT
Acknowledgements #
lmcli
is a hobby project. Special thanks to the Go community and the creators of the libraries used in this project.