# nexus-chat
A local-first terminal chat app for deep research and multi-agent work. Rust +
[ratatui], all state on your machine — SQLite per space, files and artifacts in
space directories, model-created web apps served from localhost.
## Features
- **Chat** over any configured backend: OpenRouter, OpenAI, OpenCode Zen/Go,
and Codex — one merged model list, per-model reasoning-effort control
- **Deep research** (`/research <topic>`): a conversational scoping survey →
a plan of questions with why/angles/sources briefs → parallel searcher
agents → synthesis → critic → verifier → writer, with a live activity view
and `/steer` mid-run. `/research! <topic>` runs ungated
- **Swarm** (`/swarm`): a moderator-conducted multi-persona roundtable that
iterates toward consensus
- **Watches** (`/watch`): standing research jobs that re-run daily
- **Spaces**: per-project context (instructions, memory, imported files,
scripts, apps) with embeddings-backed semantic file search
- **Skills**: reusable instruction packs (SKILL.md) with sandboxed Python
virtualenvs, installable from chat
- **Tools for the model**: nine consolidated tools — `search` (web/academic/
discussion), `fetch_url` (with PDF/YouTube extraction), `batch` (multi-op
calls), `research_lookup`, `files`, `app` (build & edit web apps served on
`http://localhost:8642`), `scripts`, `skills`, and `media` (image/video
generation with ffmpeg transforms)
- **Terminal ergonomics**: markdown rendering, image display, @-file
autocomplete, mouse selection → copy, context breakdown, compaction,
incognito mode, per-session history
## Build & run
```sh
cargo build --release
./target/release/nexus-chat
```
Keys come from the config file or env (`OPENROUTER_API_KEY`,
`OPENAI_API_KEY`, `OPENCODE_API_KEY`), or `/login` in-app. On first launch a
key is enough; models are fetched from the catalogs. Extra tooling (all
optional): `tesseract` for local OCR, `ffmpeg` for video transforms, an
`ollama` instance for local embeddings/OCR.
## Commands
Type `/` in the composer for autocomplete. Aliases in parentheses.
| `/new` (`chat`, `clear`) | start a new session |
| `/session` (`history`, `resume`, `switch`) | browse/switch sessions |
| `/space` (`project`, `workspace`) | switch spaces |
| `/model` (`llm`) | pick a model, set reasoning effort |
| `/login` (`key`) | log into a backend |
| `/research <topic>` | conversational deep research (see above) |
| `/research! <topic>` | same, no survey/approval gates |
| `/watch` | standing research, re-runs every 24h |
| `/swarm` (`panel`) | multi-persona roundtable |
| `/files` (`images`, `scripts`, …) | browse space files / images / scripts |
| `/apps` (`webapps`) | view model-created web apps |
| `/skills` | manage skills |
| `/usage` (`analytics`, `costs`) | token/cache/cost analytics by backend & model (←/→ for 24h/7d/30d/all windows) |
| `/compact` (`summarize`) | summarize old messages into a digest |
| `/config` (`settings`, `stats`) | settings, footer toggles, sampling params |
| `/web` | toggle search-first cited answering |
| `/export` (`save-report`) | write the research report + sources to a file |
| `/incognito` | toggle no-persistence mode |
| `/quit` | quit |
| `<skill-name>` | arm a skill for the next message |
## Keybindings
| `Enter` | send (Shift/Ctrl+Enter inserts a newline) |
| `Esc` | stop the streaming response / clear the composer |
| `Ctrl+C` | quit |
| `Ctrl+V` | paste (bracketed paste) |
| `Ctrl+Shift+C` / `Ctrl+X` | copy / cut composer selection |
| `Ctrl+A` | select all in composer |
| `Ctrl+Backspace` | delete previous word |
| `Ctrl+R` | expand/collapse reasoning traces |
| `Ctrl+T` | expand/collapse tool-call detail blocks |
| `Ctrl+G` | context breakdown (system/memory/skills/conversation) |
| `Ctrl+N` | toggle incognito |
| `Ctrl+O` | open a session-link message under the selection |
| `Ctrl+↑` | live research activity view (Ctrl+X there stops the job) |
| `PageUp` / `PageDown` | scroll |
| mouse drag | select + copy; `p` / `x` pin / discard the cited source under the selection |
While a research survey or plan approval is pending, Enter in that session
answers the gate; a reply with edits is folded in once by the approval agent.
## Architecture
```
src/
├── main.rs bootstrap: config, space, db, App, event loop
├── app/ the state machine (one module per feature)
│ ├── mod.rs App struct, popups, settings, run_command, gate plumbing
│ ├── chat.rs request lifecycle: history build, streaming, tool loop
│ ├── research.rs the research pipeline: survey → plan → searchers → …
│ ├── swarm.rs multi-persona roundtable
│ ├── watches.rs standing research jobs
│ ├── sessions.rs, spaces.rs, models.rs, backends.rs, memory.rs
│ ├── files.rs, images.rs, scripts.rs, apps.rs space artifacts
│ ├── skills_popup.rs, compaction.rs, export.rs, copy.rs, transcribe.rs
│ └── tests.rs app-level integration tests
├── provider/
│ ├── mod.rs message shapes, tool-call wire format, events
│ └── openrouter.rs one client for all OpenAI-wire backends (OR/OpenAI/…)
├── tools.rs the model's tools: search, fetch, python, video, apps…
├── tools (cont.) skills.rs, extract.rs (PDF/OCR/text), citations.rs
├── db.rs SQLite: sessions, messages, citations, model prefs
├── appserver.rs localhost static server for model-created apps (port 8642)
├── ui/ ratatui rendering: history, popups, theme, markdown
├── input.rs composer, @-autocomplete, command catalog
├── events.rs key/mouse handling, clipboard
└── config.rs, space.rs credentials, spaces layout
```
Data lives under the XDG data dir: `spaces/<space>/` holds the per-space
SQLite db, files, scripts, apps, and generated media.
## Development
```sh
scripts/check.sh # fmt + clippy (-D warnings, pedantic) + cargo-audit + tests
cargo test --bin nexus # 428 tests, no network needed
```
## Known limitations
- The research survey section is always visible, not collapsible.
- The survey's first-round questions are generated from the topic alone — the
concurrent known-chunks/web-survey context arrives after round 1.
- A plan rework presented within the same second overwrites the plan file
(same timestamped name).
[ratatui]: https://github.com/ratatui/ratatui