nexus-chat 0.1.0

A local-first terminal chat app for deep research and multi-agent work
# 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.

| Command | What it does |
|---|---|
| `/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

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