# solvit

Solve or fix a code file from your terminal. Point `solvit` at any OpenAI-compatible LLM endpoint,
give it a file, and it rewrites the file with the solution.

```bash
solvit solve app.js
```

## Contents

- [Install](#install)
- [Setup](#setup)
- [The app](#the-app)
- [Usage](#usage)
- [Chat](#chat)
- [Options](#options)
- [How it works](#how-it-works)
- [Troubleshooting](#troubleshooting)
- [Uninstall](#uninstall)
- [License](#license)

## Install

Requires Node.js 18 or newer. There are no dependencies.

In your project folder (works anywhere, no root needed, e.g. CodeChef containers):

```bash
npm i solvit-cli
./node_modules/.bin/solvit solve index.js
```

To type just `solvit`, add the local bin folder to your PATH once per terminal:

```bash
export PATH="$PWD/node_modules/.bin:$PATH"
solvit solve index.js
```

Or install it globally if you have permission:

```bash
npm install -g solvit-cli
```

If the global install fails with `EACCES`, use the local install above.

## Setup

Just run `solvit solve <file>`. The first time, it asks for your endpoint, API key and model and saves
them to `~/.solvitrc` (readable only by you). Run `solvit init` any time to change them.

Or set environment variables instead (they win over the saved file). Add these to `~/.bashrc` or `~/.zshrc`:

```bash
export SOLVIT_BASE_URL=https://your-endpoint.example.com   # with or without /v1
export SOLVIT_API_KEY=sk-...
export SOLVIT_MODEL=your-model                             # optional
```

If those aren't set, it falls back to the first of each that exists:

| Setting | Order checked |
|---|---|
| Base URL | `SOLVIT_BASE_URL`, `~/.solvitrc`, `OPENAI_BASE_URL`, `GOOGLE_GEMINI_BASE_URL` |
| API key | `SOLVIT_API_KEY`, `~/.solvitrc`, `OPENAI_API_KEY`, `GEMINI_API_KEY` |
| Model | `--model` flag, `SOLVIT_MODEL`, `~/.solvitrc`, then `auto/best-coding` |

Check what it picked up (the key is masked):

```bash
solvit config
```

The endpoint must accept `POST /v1/chat/completions` in the OpenAI format. Most gateways and routers
(OmniRoute, LiteLLM, OpenRouter, vLLM, Ollama) do.

## The app

```bash
./node_modules/.bin/solvit        # or just: solvit
```

Running `solvit` with no command in a terminal opens a full-screen app with two sections, **Solve** and **Chat**
(switch by clicking the **Solve** / **Chat** tabs in the header, or with `Tab` on the Solve screen and `Esc` in Chat).

**Solve** lists every file in the folder you ran it from, including subfolders like `src/`. Tick the files you
want solved, optionally type a message for the model, and press `Enter` (or click the **▶ Solve** button in the top-right corner, which also shows how many files are ticked and turns into **■ Cancel** while it runs). Every
ticked code file is rewritten right away, and each file shows its result: `✓ rewritten`, `= no changes`, or the reason
it was not changed.
Data files (`.csv`, `.tsv`, `.jsonl`, `.parquet`, `.xls`/`.xlsx`) are **reference only**: tick them along with your code
file and the model sees their contents, but they are never rewritten.

- The other ticked files are sent along as read-only reference, so the model sees how your files fit together.
- Rewrites happen immediately and are not backed up; there is no undo for the Solve screen. Use `--print`
  on the single-file command below first if you want to preview an answer before it overwrites anything.
- Hidden from the list: `node_modules`, dotfiles, `.env*`, keys and certificates, lock files and logs. Binary and
  very large files are shown greyed out and cannot be ticked. A run is limited to 150 KB in total.
- If a reply has no code, looks cut off, or the request fails, that file is left exactly as it was.

| Key | Does |
|---|---|
| `↑` `↓` `PgUp` `PgDn`, mouse wheel | Move and scroll |
| `Space` or click | Tick or untick a file (on a folder: everything inside) |
| `←` `→` or click a folder | Collapse or open a folder |
| `Enter` | Solve the ticked files |
| `/` or `Ctrl+F` | Search files (`Esc` clears) |
| `m` | Type a message for the model |
| `a` / `n` | Tick all / untick all |
| `Esc` | Cancel a run in progress |
| `Tab` | Switch to Chat (in Chat, `Esc`, the **Solve** tab, or `/solve` brings you back) |
| `q` or `Ctrl+C` | Quit |

Everything you tick is sent to the model endpoint you configured, so don't tick files you can't share with it.
Without a terminal (scripts, pipes) `solvit` prints its help instead.

## Usage (single commands)

```bash
solvit solve main.py                         # solve/fix the file and overwrite it
solvit solve main.py --print                 # show the answer, leave the file alone
solvit solve main.py -m "use recursion"      # add your own message / instructions
solvit solve main.py -c docs/ -c schema.sql  # give it reference files or folders
solvit solve main.py --model gpt-4o           # pick a model for this run
solvit problem                               # paste the task description (Ctrl+D to finish); used by later solves
solvit undo main.py                          # restore main.py from main.py.bak
solvit init                                  # set or change endpoint, key and model
```

Besides your file, `solvit solve` sends the text saved by `solvit problem` (if you ran it) and any `-c` files
you pass, such as a dataset. It prints what it is sending. Nothing else in the folder is sent. Delete
`.solvit-problem.txt` to drop the saved description.

A typical file is a problem statement in comments with an empty or broken function. `solvit` completes or
fixes it:

```js
// Return the sum of the even numbers in arr
function sumEven(arr) {
  let total = 0;
  for (let i = 0; i <= arr.length; i++) {
    if (arr[i] % 2 == 1) total += arr[i];
  }
  return total
}
```

After `solvit solve sum.js` the loop bound and the even check are fixed, and the original is kept as
`sum.js.bak`.

## Chat

```bash
solvit chat
```

A full-screen, clickable chat in the terminal. The first time, it asks you to log in or sign up (username and
password). Messages arrive live.

- **Header:** product name and version, how many people are online, and who you are.
- **Left side:** a search box, two tabs (**Global** and **Inbox**, with an unread count), then
  **CONVERSATIONS** (name, last message, time, unread badge, online dot) and **PEOPLE**. Click a name to
  start a chat, or the **+** button to search for someone.
- **Right side:** the open conversation as message rows with a coloured initial, the sender's name and time.
  **Seen** appears under your last message once they have read it, and **typing…** shows while they type.
  The bordered box at the bottom is where you type.
- **Footer:** the keyboard shortcuts and the connection status.
- **Solvit AI:** the pinned **✦ Solvit AI** row under the tabs opens a chatbot you can ask anything: code
  questions, explanations, debugging. It streams its answer as it writes, formats code blocks, and remembers
  the conversation (see below).

On tall terminals (24+ rows, 70+ columns) the screen is drawn inside a rounded frame; on shorter ones it
switches to a compact layout with the same features. It uses 24-bit colour when your terminal supports it
(`COLORTERM=truecolor`), 256 colours otherwise, and plain text with `NO_COLOR=1`.

| Do this | To |
|---|---|
| Click a tab, a chat or a name | Switch Global / Inbox / Solvit AI, open a chat |
| `Tab` / `Shift+Tab` | Cycle Global, Inbox, Solvit AI |
| `↑` `↓` | Move between your chats |
| Mouse wheel, `PgUp` `PgDn` | Scroll messages |
| `Ctrl+F` | Find a person or chat (`Esc` clears) |
| `Enter` | Send |
| `/dm <name>` `/inbox` `/global` `/ai` | Open a chat or switch tab |
| `/clear` | In Solvit AI: forget the conversation |
| `Esc` | In Solvit AI: stop an answer that is still being written |
| Click **+** in the AI box, or `Ctrl+O`, or `/attach` | In Solvit AI: attach files to your question |
| `/attach <path>` / `/detach` | Attach one file by path / remove all attachments |
| `/users` | List everyone (● = online) |
| `/logout` | Sign out on this machine |
| `/quit` or `Ctrl+C` | Leave |

The mouse is optional: everything works from the keyboard. With the mouse on, hold `Shift` while dragging to
copy text. `solvit chat logout` signs out without opening the screen. Chat needs a chat server: see
[server/README.md](server/README.md) for running your own, and set `SOLVIT_CHAT_URL` to choose one.
Anyone on the server can read the Global room, so don't post secrets.

**Attaching files to Solvit AI:** click the **+** in the AI message box (or press `Ctrl+O`) to open a file
picker for the folder you started `solvit` in. Type to filter, `Space` or a click ticks a file, `Enter` attaches
it. Attached files show above the box with a **✕** to remove them, and their current contents are sent to the
model with your next question, so you can ask things like "why does this fail?". Up to 100 KB per question.
Secrets (`.env*`, keys, certificates), lock files, `node_modules`, binary files and files over 100 KB are never
offered. The chat shows only the file names under your question, and the saved history keeps the names but never
the contents. Files stay in the model's context for later questions in the same session, not after a restart.

**About Solvit AI:** your questions go to the same model endpoint that solving uses (not to the chat server), so
other chat users never see them. The last 20 messages are sent along with each question so it keeps context.
The conversation is saved in `~/.solvit-ai.json` on your machine (readable only by you); `/clear` deletes it.
It needs a working model endpoint, so check `solvit config` if it reports an error.

## Options

| Option | Meaning |
|---|---|
| `--print` | Show the answer without modifying the file. By default the file is replaced and a `<file>.bak` copy is saved first. |
| `-m, --message <text>` | Extra instructions for the model. |
| `-c, --context <path>` | A file or folder given to the model as reference. Repeatable. `node_modules`, `.git`, `dist`, `build` and `.next` are skipped; total context is capped at about 200 KB. |
| `--model <name>` | Model to use for this run. |
| `--no-stream` | Wait for the full reply instead of streaming it (only affects `--print`). |
| `-h, --help` | Show help. |
| `-v, --version` | Show the version. |

## How it works

1. Reads the file (and any `--context` files).
2. Sends them to your endpoint with an instruction to complete or fix the code.
3. In the default mode, takes the largest code block from the reply, saves `<file>.bak`, and overwrites
   the file. With `--print` it prints the reply and touches nothing.

## Troubleshooting

| Problem | Fix |
|---|---|
| `no endpoint set` | Run `solvit init` (needs a terminal), or export `SOLVIT_BASE_URL` and `SOLVIT_API_KEY`. `solvit config` shows what's in use. |
| `API returned 401` | The key is wrong or expired for that endpoint. |
| `API returned 404` | Wrong base URL or model name. Try the URL without a trailing path, or check the model list. |
| `model did not return a code block; file left unchanged` | Retry, add `-m "reply with the full file in one code block"`, or use `--print`. |
| `file is empty, nothing to solve` | Put the problem or the code in the file first. |
| Wrong result | Run `solvit undo file`, then retry with `-p` to say what you want. |

## Privacy and safety

- The contents of your file, and of any `--context` files, are sent to the endpoint you configured. Don't
  point `solvit` at files you can't share with that provider.
- Chat messages and passwords go to the chat server you connect to; with a plain `http://` URL they are unencrypted.
- Use `https://` endpoints. A plain `http://` URL sends your key and code unencrypted.
- `~/.solvitrc` stores your API key in plain text (mode 600). Delete it to forget the key.
- Review the result before running it. A `.bak` is saved, but the file is still replaced.

## Uninstall

```bash
npm uninstall solvit-cli       # local install
npm uninstall -g solvit-cli    # global install
rm ~/.solvitrc             # forget saved settings
```

## License

MIT
