Metadata-Version: 2.4
Name: linus-cli
Version: 0.1.0
Summary: AI agent CLI with pluggable tools, powered by free NVIDIA NIM models
License-Expression: MIT
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai>=1.40
Requires-Dist: httpx>=0.27
Requires-Dist: typer>=0.12
Requires-Dist: textual>=0.80
Requires-Dist: rich>=13.7
Requires-Dist: ddgs>=9.0
Requires-Dist: psycopg[binary]>=3.2
Requires-Dist: pgvector>=0.3
Requires-Dist: python-dotenv>=1.0
Requires-Dist: pillow>=10.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Provides-Extra: browser
Requires-Dist: playwright>=1.40; extra == "browser"
Provides-Extra: lint
Requires-Dist: ruff>=0.6; extra == "lint"
Dynamic: license-file

# 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)

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

```bash
mkdir -p ~/.agent-cli && cat > ~/.agent-cli/.env <<'EOF'
NVIDIA_API_KEY=your_key_here
EOF
```

Then run `linus` from anywhere. Optional extras:

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

```bash
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_shell`** runs 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; native `cmd.exe`/PowerShell will run the app fine, but shell
>   commands the model writes may not match what's available in your shell.

## Usage

```bash
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-paste` or `xclip`, 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:

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

```bash
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):

```python
# ~/.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` (or `AGENTS.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`:

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

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