Add a Textual chat interface with threaded SSE streaming, slash commands, interrupt handling, and bare nexus dispatch. Package it behind the tui extra, document usage, and cover command routing, dependencies, and headless interaction with tests.
10 KiB
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 -ExecutionPolicy Bypass -File .\install-windows.ps1 # one-time native install
ncp web # memory :8001 + backend :8000 + UI
Linux full stack (dev):
./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.
macOS (community-supported):
./install-macos.sh # one-time: Homebrew packages + venv + web build, via bin/sync.py
./launch_nexus.sh # same script as Linux - it's plain bash, no Linux-only calls
No bundled Ollama binary (Linux x86-64 only) and no XFCE desktop branding — both
already no-op on macOS via bin/sync.py's linux_stage(). Ollama is instead the
Homebrew-installed native binary, picked up automatically because
OllamaManager falls back to ollama on PATH when the bundled binary is
absent; that gets full Metal GPU acceleration with no extra config.
Individual services via CLI:
# 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 (nexus / ncp) — start/stop services with PID tracking, plus
terminal access to the same features as the web UI (REST API on :8000):
./management/nexus-cli.sh start # starts backend + frontend
./management/nexus-cli.sh stop
./management/nexus-cli.sh start --backend|-b / --frontend|-f / --memory|-m
# Interactive TUI (Hermes/OpenClaw-style; needs pip install 'nexusos-ai[tui]'):
nexus # bare command opens the Textual chat TUI
nexus tui # same, explicit
# Feature one-shots (dispatch to nexusos_cli/nexus_api.py — httpx):
nexus chat send "<message>" # stream a reply (POST /chat/stream)
nexus memory list|add <text>|rm <id>
nexus playbook list|show <id> # first playbook (*) is the active system prompt
nexus history [query] # recent conversations
nexus monitor # ASCII status dashboard (no prompt)
The interactive TUI lives in nexusos_cli/tui_app.py (Textual, optional extra).
One-shot subcommands and nexus monitor remain for scripts. The CLI package is
nexusos_cli/ (what the wheel ships); management/ keeps desktop-only pieces —
shell wrappers, Tk control panel, XFCE panel wiring.
management/controlpanel.py (tkinter GUI, wired into the XFCE panel via
bin/panel/nexus-popup.py) stays.
Checks (the release gate):
./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:
cd interface/web && npm run lint
Frontend build:
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, CPU/Windows, or — via an explicit sys.platform == "darwin" check, since os.name alone can't tell macOS apart from Linux — requirements-base.txt with no overlay at all for macOS, 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/chatwas removed). After each exchange the stream endpoint calls the Memory Service to auto-extract persistent facts./playbooks— CRUD for playbooks stored as YAML files indata/playbooks/viasynapse/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./settingsand/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.
Code Tracks (synapse/tools.py + synapse/code_run.py)
Two separate tools, split by where the code runs:
render_preview— validates markup and returns a fence the chat renders in an opaque-originsandbox="allow-scripts"iframe. Nothing executes server-side. Languages:PREVIEW_LANGSinsynapse/tools.py, mirrored byinterface/web/src/preview/languages.js.run_snippet— compiles and runs a single file on the host viasynapse/code_run.py, and returns a ```nexus-run fence carrying the source and its captured output. Languages:RUN_LANGSinsynapse/code_run.py, mirrored byinterface/web/src/preview/run-langs.js.
Each pair of registries is asserted equal by tests/test_tools.py — nothing
couples them at runtime, so drift fails the check gate instead of silently
degrading in the chat.
run_snippet is an action tool: action_tool_policy gates it (off by
default, ask = per-call Approve/Deny in chat). Read the code_run.py module
docstring before touching it — it runs code as the current user and is explicit
about which of its five layers are load-bearing and which are only a tripwire.
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 stdoutruntime/logs/ollama.log,runtime/logs/chat.logruntime/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 |