# lmcli v0.7.0 – TUI harness with smooth performance up to 1M context

> Source: <https://codeberg.org/mlow/lmcli>
> Published: 2026-06-13 06:19:33+00:00

- Go 100%

|
|

[screenshots](/mlow/lmcli/src/branch/main/screenshots)[.gitignore](/mlow/lmcli/src/branch/main/.gitignore)[CHANGELOG.md](/mlow/lmcli/src/branch/main/CHANGELOG.md)[config.sample.yaml](/mlow/lmcli/src/branch/main/config.sample.yaml)[go.mod](/mlow/lmcli/src/branch/main/go.mod)[go.sum](/mlow/lmcli/src/branch/main/go.sum)[LICENSE](/mlow/lmcli/src/branch/main/LICENSE)[main.go](/mlow/lmcli/src/branch/main/main.go)[README.md](/mlow/lmcli/src/branch/main/README.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](/mlow/lmcli/src/branch/main/screenshots).

## Features

- Buttery smooth at 1M+ tokens (sliding-window rendering)
- Composable, nesting
[Agents](#user-content-agents) - Custom
[tools](#user-content-tools)(exec-only, MCP coming soon) [Sandbox](#user-content-sandbox)tool 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](/mlow/lmcli/src/branch/main/config.sample.yaml) for the full example configuration.

### Defaults

Defaults which apply to new `lmcli chat`

and `lmcli new`

sessions.

```
# ~/.config/lmcli/config.yaml
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.

```
# ~/.config/lmcli/config.yaml
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 search`Read`

- 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 execution`SubAgent`

- Spawn a sub-agent - see[Agents](#user-content-agents)`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](#user-content-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.*

```
# ~/.config/lmcli/config.yaml
tools:
  # Override a built-in tool
  - name: WebReader
    config:
      backend: https://firecrawl.example.com/v1
      apiKey: fc-...

  # Custom external tool
  - 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.

```
# ~/.config/lmcli/config.yaml
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](https://github.com/containers/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.

```
# ~/.config/lmcli/config.yaml
sandbox:
  # Whether to use the sandbox, default false
  enabled: true
  # Whether to remap the working directory to /workspace in the sandbox
  remapWorkspace: true
  # Paths to map from the host into the sandbox
  bindDirs:
    - /home/user/.config:/home/user/.config:ro
    - /home/user/.cache:/home/user/.cache:ro
  # Paths to persist across lmcli instances and tool invocations
  # Stored in ~/.local/share/lmcli/sandbox/<path>
  persistDirs:
    - /tmp
```

### Misc

Various settings that affect `lmcli`

's behavior.

```
# ~/.config/lmcli/config.yaml

# Conversation title generation
conversations:
  titleGeneration:
    # Defaults to defaults.model
    model: optional
    # set to agent with custom prompt for title generation.
    # Agent must respond with {"title": "<title>"} JSON
    # Defaults to built-in agent, see pkg/lmcli/tasks/title.go
    # agent: title-generator
    effort: none
    maxTokens: 1024

# Syntax highlighting by Chroma: https://github.com/alecthomas/chroma
chroma:
  # Refer to: https://github.com/alecthomas/chroma/tree/master/styles
  style: onedark
  # - `terminal` - 8 colors
  # - `terminal16` - 16 colors
  # - `terminal256` - 256 colors
  # - `terminal16m` - true color (default)
  formatter: terminal16m

tui:
  hideHelpHint: false
```

## Usage

``` bash
$ 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.

``` bash
$ lmcli code
```

Start a new conversation, imperative style (no tui):

``` bash
$ lmcli new "Help me plan meals for the next week"
```

Send a one-shot prompt (no persistence):

``` bash
$ 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`

:

```
# Emulate scrolling by sending up and down keys if these commands are running in the pane
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.
