AI agent CLI with pluggable tools, powered by free NVIDIA NIM models
Project description
agent-cli
A Claude Code-style AI agent for your terminal, powered by NVIDIA NIM and OpenRouter models. Full-screen TUI (streaming, never blocks), pluggable tools, and long-term chat memory with semantic recall (Postgres + pgvector).
Installation
Requires Python 3.11+. Chat memory (optional) needs Docker; OmniRoute (optional — a self-hosted router across multiple LLM providers) needs Node.js 18+.
Quick install (Linux, macOS, Windows)
pip install linus-cli
That's the whole install — same idea as npm install -g @anthropic-ai/claude-code
for Claude Code, just via pip instead of npm. Add your API key afterward:
mkdir -p ~/.agent-cli && cat > ~/.agent-cli/.env <<'EOF'
NVIDIA_API_KEY=your_key_here
EOF
Then run linus from anywhere. Optional extras:
docker compose up -d # chat memory (Postgres + pgvector) — needs Docker
npm install -g omniroute # self-hosted multi-provider router — needs Node.js
From source (for development)
git clone https://github.com/linuso71/linuscli.git # requires repo access — it's private
cd linuscli
python3 -m venv .venv # Windows: py -m venv .venv
.venv/bin/pip install -e . # Windows: .venv\Scripts\pip install -e .
cp .env.example .env # Windows: copy .env.example .env — then paste your API key
Does this run on Windows? The core app (Python + the Textual TUI) is cross-platform and should run natively — I don't have a Windows machine to verify on directly, so this is based on code review rather than a live test, but there's nothing OS-specific in the agent loop, tool system, or config. Two known gaps on native Windows:
- Clipboard image paste (Ctrl+V) currently only works on Linux (shells out to
wl-paste/xclip) — pasted images aren't picked up on Windows yet. Text paste and file attachments both work fine everywhere.run_shellruns whatever command the model writes verbatim, and its instructions assume Unix-style commands (ls,grep,cat, …) — it'll work best in a POSIX-like shell. WSL2 gives the smoothest experience on Windows; nativecmd.exe/PowerShell will run the app fine, but shell commands the model writes may not match what's available in your shell.
Usage
linus # launch the TUI
linus "what's new in python 3.14?" # one-shot mode
linus -m meta/llama-4-maverick-17b-128e-instruct "hi" # override model
linus -m openrouter:qwen/qwen3-coder:free "hi" # use OpenRouter directly
linus models # list models your configured keys can use
TUI commands
| Command | What it does |
|---|---|
/help |
show help |
/new |
start a new session |
/history |
list saved sessions |
/resume <id> |
continue a past session |
/recall <query> |
semantic search over past conversations |
/model |
pick a model from a searchable list (saved for future sessions) |
/model <id> |
switch chat model directly |
/exit |
quit |
Esc cancels the current response. Ctrl+Q quits (Ctrl+C copies selected text in the prompt box instead, matching your terminal's usual behavior).
Sessions
/history (or /resume with no id) opens an interactive picker — type to
filter, ↑/↓ to navigate, Enter to resume a past conversation.
Attachments: files and images
Three ways to attach, all shown as removable chips above the input:
- 📎 button (or
/attach) — browse the directory tree and pick a file - Paste a file path — a pasted path to an existing file attaches it instead of inserting text
- Ctrl+V — pastes an image straight from the system clipboard (screenshots; needs
wl-pasteorxclip, Linux only for now — see the Windows note under Installation); pastes plain text into the prompt otherwise
Text files are inlined into the prompt. Images go to the model as real image
input when it supports vision (llama-4-maverick, qwen3.5, the -vl models, …);
for text-only models a free NVIDIA vision model
(nemotron-3-nano-omni-30b-a3b-reasoning, configurable as vision_model)
describes the image first and the description is inlined — so image paste
works with every chat model.
One-shot mode: linus -f screenshot.png "why did this build fail?" (repeatable).
Modes: discuss → plan → code
The TUI starts in ⚡ code mode, ready to build (like Claude Code). Click the
mode badge next to the input (or press Shift+Tab, or type /mode discuss)
to move through the workflow:
| Mode | Tools available | Use it to |
|---|---|---|
| 💬 discuss | web_search, list_dir, read_file, search_code (read-only) | talk the idea through — nothing can be modified |
| 📋 plan | same read-only tools, plan-focused instructions | let the agent explore the project and write a step-by-step plan |
| ⚡ code | everything (write, edit, git push) | implement the agreed plan |
Tool restrictions are enforced twice: restricted tools aren't offered to the
model at all, and any hallucinated call is refused at execution. One-shot mode
defaults to code; use linus --mode plan "..." to override.
Permissions
Tools are classified by risk: safe (search/read — never confirmed),
write (write_file/edit_file), risky (git_push — leaves your machine).
The permission badge next to the mode badge cycles three levels (also
/permission <level>, or -p in one-shot mode):
| Level | Behavior |
|---|---|
| 🔒 ask (default) | confirm every write and risky tool |
| ⚠ risky | file writes run automatically; confirm only risky tools |
| 🔓 auto | everything runs without prompts |
The ▶ Send button next to the input submits your message; while the agent is working it turns into ■ Cancel (same as pressing Esc).
Built-in tools
- web_search — DuckDuckGo search (free, no key)
- git_push —
git add .+ commit + push (risky: always confirmed unless auto) - list_dir / read_file / search_code — explore files in the directory where you launched
linus - write_file / edit_file / delete_file — create, edit, and delete files (confirmed at the ask level)
- run_shell — run a shell command in the current directory (risky: always confirmed unless auto)
- browser_navigate / browser_snapshot / browser_click / browser_fill / browser_screenshot / browser_close — drive a real headless browser for end-to-end / UI testing (like GitHub Copilot's agent mode)
Browser automation (optional)
Linus can open a real browser, read the page, click, and type — so it can test a web UI end-to-end, not just build it. Enable it once:
.venv/bin/pip install -e ".[browser]"
.venv/bin/playwright install chromium
Then, in code mode: "start the dev server and test the login page in a browser" — linus navigates, snapshots the page (title + interactive elements + text), fills fields, clicks buttons, and can screenshot. If Playwright isn't installed the tools return an install hint instead of failing. Cleanup happens automatically on exit.
Filesystem access is sandboxed to the directory you run linus from — the model
cannot read or write anything outside it.
Run it from anywhere
printf '#!/bin/sh\nexec "%s/.venv/bin/python" -m agent_cli.main "$@"\n' "$PWD" > ~/.local/bin/linus
chmod +x ~/.local/bin/linus
mkdir -p ~/.agent-cli && cp .env ~/.agent-cli/.env # global API key
Then linus works in any directory, with its file tools scoped to that directory.
A .env in the current directory overrides the global one (per-project keys/models).
Adding a tool
Drop a file in agent_cli/tools/ (or ~/.agent-cli/tools/ for personal tools —
no reinstall needed):
# ~/.agent-cli/tools/weather.py
from agent_cli.tools.base import tool
@tool # add requires_confirmation=True for side-effectful tools
def get_weather(city: str) -> str:
"""Get the current weather for a city.
Args:
city: City name.
"""
return "sunny, 24°C"
That's it — the schema the model sees is generated from the type hints and docstring, and the file is auto-discovered at startup.
Project memory & context management
Linus manages long sessions the way Claude Code / Codex do:
- Project memory file — drop a
LINUS.md(orAGENTS.md/CLAUDE.md) in your repo with conventions, build commands, and facts. It's found automatically (searching up to the git root) and injected into every session's system prompt. - Auto-summarization — when the conversation exceeds a token budget
(
context_token_budget, default ~16k), older turns are condensed into a summary so per-request cost stays bounded. It cuts only at turn boundaries, so tool-call/result pairs are never split. - Subagents —
run_subagent(task)(code mode) delegates broad exploration to an isolated read-only agent that returns just its findings, so the many files it reads never enter the main context.
Two related savers from before: duplicate file reads keep only their newest copy in context, and a call repeated 3× is refused so weak models can't loop.
Prompt caching isn't something linus can enable on the free NVIDIA tier — NIM doesn't expose it. Some OpenRouter models cache automatically server-side (e.g. DeepSeek); linus keeps its system prefix stable across turns so those models benefit where supported, but there's no client-side knob to add.
Configuration
Environment (or .env): NVIDIA_API_KEY, OPENROUTER_API_KEY, DATABASE_URL,
AGENT_CHAT_MODEL. In the TUI, /model shows separate NVIDIA and OpenRouter
categories. For a direct CLI override, prefix an OpenRouter ID with openrouter:.
Optional ~/.agent-cli/config.toml:
chat_model = "qwen/qwen3-coder-480b-a35b-instruct"
embedding_model = "nvidia/llama-3.2-nv-embedqa-1b-v2"
max_tool_iterations = 10
recall_snippets = 3
Free catalogs rotate over time — run linus models to see what your keys can use
and switch with /model or config.
Development
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest tests/
Architecture
agent_cli/agent.py— UI-agnostic async agent loop; yields typed events (TextDelta,ToolCallStarted, …). The TUI and one-shot printer are two consumers of the same stream.agent_cli/tui/— Textual app. All LLM/tool/DB work runs off the UI event loop, so the input never freezes.agent_cli/memory.py— best-effort memory: if Postgres is down or embedding fails, the CLI warns once and keeps working without recall.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linus_cli-0.1.0.tar.gz.
File metadata
- Download URL: linus_cli-0.1.0.tar.gz
- Upload date:
- Size: 86.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72618d8d3403bec56581b44ae3808e629ed150060d061e5d3ed9fd5d80da5a72
|
|
| MD5 |
bf2715e3c86e88861f45a3961e8b8a8c
|
|
| BLAKE2b-256 |
6e015fd7368d24317fc9b4364f1c8b8ff58782d7c32b6824489f38ef35441b8e
|
File details
Details for the file linus_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: linus_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 80.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a8e94d69deec8d04fd35d3ab0da8d8c7c6ac7dac4bfbb543bc73ff7e2d46896
|
|
| MD5 |
551bcc42483ef77e6a9c56a8eae5001d
|
|
| BLAKE2b-256 |
c73f7bc8e5c737490309d75db5bd50eb096c76b12a5174452de4bdef7f5e89db
|