The CLI shipped from `management/`, which also holds desktop-only pieces (the Tk control panel, the XFCE panel wiring, the shell wrappers). Packaging that directory meant the wheel either dragged in tkinter or shipped a broken import. Split it: `nexusos_cli/` is what the wheel ships and what `nexus`/`ncp`/ `nexusos` dispatch to, `management/` keeps the desktop half. Alongside the move: * hatch_build.py decides the interface/web/dist include at build time. dist/ is gitignored, so a static force-include aborts `pip install -e .` on a fresh clone - before the reader reaches the `npm run build` step. Editable installs now skip a missing dist; wheels and sdists hard-error naming the command to run. * synapse/proc_util.py gives frontend_manager and ncp process inspection and termination without psutil, which became an optional extra when the wheel landed. It routes around Windows having no signals, where os.kill(pid, 15) is an unblockable TerminateProcess rather than a polite request. * nexusos_cli/monitor.py adds `ncp monitor`, an ASCII dashboard with no curses or rich dependency so it works in Termux, plain SSH and Windows Terminal. Collector and renderer are separate so tests feed fixtures, no stack needed. * tests/test_packaging_deps.py fails the gate when synapse or nexusos_cli import a distribution pyproject does not declare, and when an optional dependency is imported at module scope instead of lazily. * bin/check.sh now builds the wheel, twine-checks it, and asserts the compiled UI and seed playbooks are actually inside it. A wheel that builds but ships no dist/ serves a blank page, which only shows up after release. tests/test_nexus_api.py moves to tests/ with the module it covers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
132 lines
8.2 KiB
Markdown
132 lines
8.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## What is NexusOS
|
|
|
|
NexusOS is a local AI assistant platform. It runs a Python/FastAPI backend (Synapse) that interfaces with a locally bundled Ollama instance, a dedicated memory microservice, and a React/Vite frontend. All AI inference runs through Ollama on `localhost`; no external AI provider is configured or called.
|
|
|
|
## Running the Project
|
|
|
|
NexusOS runs **single-process**: the Synapse backend on port 8000 serves the
|
|
built web UI (`interface/web/dist`) itself, so there is no separate Vite server
|
|
at runtime. Ollama is started manually (sidebar **Start AI** / `nexus-cli.sh
|
|
start --ai`), not on backend startup.
|
|
|
|
**Windows (recommended):**
|
|
```powershell
|
|
powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 # one-time native install
|
|
ncp web # memory :8001 + backend :8000 + UI
|
|
```
|
|
|
|
**Linux full stack (dev):**
|
|
```bash
|
|
./launch_nexus.sh
|
|
```
|
|
This activates the `Promethean` venv and starts the memory service on port 8001 and the Synapse backend on port 8000 (which also serves the built UI). It additionally starts a **Vite dev server** for frontend hot-reload — a Linux-dev convenience, unlike the single-process Windows/production path where the backend serves `dist/` alone. It does **not** start Ollama.
|
|
|
|
**Individual services via CLI:**
|
|
```bash
|
|
# From nexus-core/ with Promethean venv active:
|
|
source Promethean/bin/activate
|
|
|
|
# Backend (serves the built UI at :8000 too)
|
|
uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload
|
|
|
|
# Memory service
|
|
uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload
|
|
|
|
# Frontend DEV server (hot-reload) — only when editing the UI; production is the
|
|
# built dist/ served by the backend. Run `npm run build` to refresh dist/.
|
|
cd interface/web && npm run dev
|
|
```
|
|
|
|
**Management CLI** (`ncp`) — start/stop services with PID tracking, plus terminal
|
|
access to the same features as the web UI (all via the REST API on `:8000`):
|
|
```bash
|
|
./management/nexus-cli.sh start # starts backend + frontend
|
|
./management/nexus-cli.sh stop
|
|
./management/nexus-cli.sh start --backend|-b / --frontend|-f / --memory|-m
|
|
|
|
# Feature commands (dispatch to nexusos_cli/nexus_api.py — httpx, no TUI):
|
|
ncp chat "<message>" # stream a reply (POST /chat/stream)
|
|
ncp memory list|add <text>|rm <id>
|
|
ncp playbook list|show <id> # first playbook (*) is the active system prompt
|
|
ncp history [query] # recent conversations
|
|
```
|
|
The old curses TUIs (`nexus-chat.py`, `nexus-playbook.py`) were removed in favor of
|
|
these API-backed subcommands. The CLI covers chat, memory, playbooks, and history;
|
|
the web UI and control panel expose the remaining management features.
|
|
The CLI itself lives in `nexusos_cli/` (that is what the wheel ships and what
|
|
`nexus`/`ncp`/`nexusos` dispatch to); `management/` keeps the desktop-only
|
|
pieces — the shell wrappers, the Tk control panel, and the XFCE panel wiring.
|
|
`management/controlpanel.py` (tkinter GUI, wired into the XFCE panel via
|
|
`bin/panel/nexus-popup.py`) stays.
|
|
|
|
**Checks (the release gate):**
|
|
```bash
|
|
./bin/check.sh # pytest + eslint + frontend tests + .ps1/.sh parse + wheel build
|
|
```
|
|
There is no hosted CI — the remote is self-hosted Gitea with no act_runner — so
|
|
this script *is* the gate. Run it before tagging a release.
|
|
|
|
**Frontend lint only:**
|
|
```bash
|
|
cd interface/web && npm run lint
|
|
```
|
|
|
|
**Frontend build:**
|
|
```bash
|
|
cd interface/web && npm run build
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Python venv
|
|
All Python code runs inside `Promethean/` (a local venv). Always activate it before running backend commands: `source Promethean/bin/activate`. Dependencies are layered: `requirements-base.txt` holds the GPU-agnostic core, and a thin overlay pins the right PyTorch build for the target — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-windows.txt` (CPU-only). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, or CPU/Windows requirements from the host.
|
|
|
|
### Synapse Backend (`synapse/`)
|
|
FastAPI app at `synapse/main.py`. Key responsibilities:
|
|
- `/chat/stream` — chat with Ollama; streaming uses SSE (the only chat endpoint — the non-stream `/chat` was removed). After each exchange the stream endpoint calls the Memory Service to auto-extract persistent facts.
|
|
- `/playbooks` — CRUD for playbooks stored as YAML files in `data/playbooks/` via `synapse/playbooks/store.py`.
|
|
- `/memory` — CRUD for persistent facts (proxies the same SQLite store as the memory service).
|
|
- `/models` — lists, pulls, and deletes Ollama models by proxying Ollama's HTTP API.
|
|
- `/settings` and `/ollama` — persist runtime settings and control Ollama lifecycle.
|
|
- `/conversations` — persists, retrieves, edits, deletes, and exports full chat history from SQLite.
|
|
- `/icons` — lists local application icons and applies NexusOS branding.
|
|
|
|
**System prompt assembly** (in `main.py` `chat_stream_endpoint`): the final system prompt is built by layering the active playbook instructions → reference playbook context → persistent memory facts → relevant past conversation snippets retrieved by `store.search_conversations`.
|
|
|
|
### Memory Service (`synapse/memory/`)
|
|
A separate FastAPI app on port 8001. `service.py` exposes `/memories/extract` which calls `extractor.py` — an Ollama prompt that decides whether to persist a new fact from a conversation exchange. The main Synapse backend calls this asynchronously after each streaming response. Both services share the same SQLite database (`synapse/memory/memory.db`).
|
|
|
|
### Playbook System (`synapse/playbooks/` + `synapse/playbook_manager.py`)
|
|
Playbooks are ordered records (title, goal, instructions, tags), each persisted as a `{id}.yaml` file in `data/playbooks/` by `PlaybookFileStore` (the dir is `PLAYBOOK_DIR` in `nexus_config.py`). The **first** playbook by order is the active system prompt; all subsequent playbooks are injected as reference context. `PlaybookManager` is the thin class the backend uses to retrieve them and assemble the system prompt.
|
|
|
|
### Ollama (`ollama/bin/ollama`)
|
|
A bundled Ollama binary lives at `ollama/bin/ollama`. `OllamaManager` in `synapse/ollama_manager.py` manages its lifecycle (start/stop/health-check) and selects the best available model. GPU detection uses Vulkan (`vulkaninfo`) to prefer discrete AMD/NVIDIA GPUs. The Ollama HTTP API is at `http://127.0.0.1:11434` (overridable via `OLLAMA_HOST` env var).
|
|
|
|
### Frontend (`interface/web/`)
|
|
React 19 + Vite. No routing library — `App.jsx` manages page state in a single `currentPage` useState. All API calls hit `http://localhost:8000` (configured in `src/config.js`). Built to `dist/` (gitignored) via `npm run build` and served by the backend at `:8000` — the mount is in `synapse/main.py` (`_DIST` at `/`, guarded by `is_dir()`), so `dist/` must be built for the UI to appear. Pages: Chatbot, Playbook editor, Conversation History, Models, Memory, Settings, Logs.
|
|
|
|
### Persistent Storage
|
|
Most data lands in `synapse/memory/memory.db` (SQLite, WAL mode). Tables: memory facts, conversations, messages, app settings. `synapse/memory/store.py` (`PersistentMemoryStore`) owns the schema and all queries. Playbooks are the exception — they live as YAML files in `data/playbooks/` (see Playbook System). `nexus_config.py` defines all paths; it also ensures all required directories exist on import.
|
|
|
|
### Logs & Runtime State
|
|
- `runtime/backend.log`, `runtime/frontend.log`, `runtime/memory.log` — service stdout
|
|
- `runtime/logs/ollama.log`, `runtime/logs/chat.log`
|
|
- `runtime/pids/backend.pid`, `runtime/pids/frontend.pid` — used by the management CLI
|
|
|
|
## Key Config
|
|
|
|
| Concern | Location |
|
|
|---|---|
|
|
| Ollama host | `OLLAMA_HOST` env var (default `http://127.0.0.1:11434`) |
|
|
| All filesystem paths | `synapse/nexus_config.py` `Settings` class |
|
|
| Frontend API base URL | `interface/web/src/config.js` |
|
|
| Default chat/memory models | `synapse/nexus_config.py` `DEFAULT_CHAT_MODEL` / `DEFAULT_MEMORY_MODEL` |
|
|
| Python dependencies (base) | `requirements-base.txt` |
|
|
| Python dependencies (AMD/ROCm) | `requirements-amd.txt` |
|
|
| Python dependencies (NVIDIA/CUDA) | `requirements-nvidia.txt` (generated by `bin/gen-nvidia-reqs.py`) |
|
|
| Python dependencies (Windows/CPU) | `requirements-windows.txt` |
|