Vendors curry_core.py from Athena-Pro/Curry (with the str.format()/format_map() sandbox-escape fix from https://github.com/Athena-Pro/Curry/pull/4 already applied) into synapse/, since Curry itself isn't a pip-installable package - it's meant to be pointed at via a config path, which only works from a source checkout. Vendoring a single self-contained, stdlib-only file ships it inside NexusOS's own wheel with no extra dependency to reconcile. synapse/curry_store.py opens it into a module-level singleton (curry_db) at import time, the same pattern as memory.store.store and playbooks.store.playbook_store, and main.py imports it so it's genuinely initialized at process startup - preloaded, not lazy-on-first-use. Backed by its own CURRY_DB file (nexus_config.py), separate from memory.db. NexusOS builds exactly one wheel (py3-none-any, no compiled extensions) - there is no separate Windows/macOS/Linux artifact; platform differences are handled by requirement overlays at install time, not by building different wheels. Verified the same wheel actually carries this correctly: built it, confirmed twine check passes, confirmed synapse/curry_core.py and curry_store.py are present in the archive (bin/check.sh's packaging gate now asserts this too), then installed that exact wheel into a throwaway venv and round-tripped a declare_constant/get_constant_latest call against it with no source checkout present - proving "preloaded and ready to be called" holds from the shipped artifact, not just editable-install execution. Android/Termux is unaffected by this change in either direction: it already has a separate, documented, pre-existing blocker in docs/TERMUX.md (no published Android pydantic-core wheel) that has nothing to do with Curry, which is pure stdlib and adds no new native/binary dependency. Scope: preload only, nothing wired into a chat-facing tool yet - no model or user-authored content reaches declare_function/call_function today. Verified: 216 backend tests pass (4 new in test_curry_store.py, including a regression test proving the vendored sandbox fix survived the copy); the 12 pre-existing C/C++/Rust toolchain failures are unrelated and unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11 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.
Curry (synapse/curry_core.py + synapse/curry_store.py)
curry_core.py is vendored, unmodified-except-for-one-fix, from Athena-Pro/Curry — an immutable, versioned fact store (constants, functions, model registrations, inference provenance) backed by its own SQLite file (CURRY_DB in nexus_config.py, separate from memory.db). curry_store.py opens it into a module-level singleton (curry_db) at import time — the same pattern as memory.store.store / playbooks.store.playbook_store — so it's preloaded and callable (curry_db.declare_constant(...), curry_db.call_function(...), etc.) from anywhere in the backend without extra setup. It ships inside the wheel (bin/check.sh's packaging gate asserts this) and has no external dependencies of its own. Nothing currently wires chat/model-authored content into it — it's available, not yet exposed as an action tool. Re-sync curry_core.py from upstream by hand, not by script; see the file's own docstring for what changed and why.
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 |