| Task | Command |
|---|---|
| One-shot notebook setup | cargo xtask dev |
| Start dev server | cargo xtask notebook |
| Standalone Vite | cargo xtask vite |
| Attach to Vite | cargo xtask notebook --attach |
| Full debug build | cargo xtask build |
| Rust-only rebuild | cargo xtask build --rust-only |
| Run bundled binary | cargo xtask run |
| Run with notebook | cargo xtask run path/to/notebook.ipynb |
| Build release .app | cargo xtask build-app |
| Build release DMG | cargo xtask build-dmg |
| nteract-dev MCP server | cargo xtask run-mcp |
| MCP config JSON | cargo xtask run-mcp --print-config |
Direct runt mcp (no proxy) |
./target/debug/runt mcp |
| Lint (check mode) | cargo xtask lint |
| Lint (auto-fix) | cargo xtask lint --fix |
Install sccache to share compiled artifacts across worktrees. Without it, each worktree rebuilds ~788 crates from scratch.
brew install sccache # macOSThe xtask commands auto-detect sccache and set RUSTC_WRAPPER when it's
available — no configuration needed. You'll see "Using sccache for compilation
cache" in the build output when it's active.
Use cargo-xwin when validating
Windows MSVC compile errors from macOS. A direct
cargo check --target x86_64-pc-windows-msvc does not provide the Windows SDK
or MSVC CRT headers that C dependencies need.
Install the local tools:
rustup target add x86_64-pc-windows-msvc
cargo install cargo-xwin
brew install llvm nasmThen run the Windows check through cargo-xwin:
cargo xwin check -p runtimed --lib --target x86_64-pc-windows-msvcThe first run downloads the MSVC CRT and Windows SDK into cargo-xwin's cache.
nasm is required by aws-lc-sys, and Homebrew's LLVM provides the
clang-cl/lld-link toolchain used by the cross build.
Best for first-time local setup or when you want the daemon and notebook app to come up together.
cargo xtask devThis command:
- runs
pnpm installwhen your workspace dependencies are missing or stale - runs
cargo xtask buildunless you pass--skip-build - starts the per-worktree dev daemon
- waits for the daemon to be reachable
- launches the notebook app in dev mode
For faster repeat launches:
cargo xtask dev --skip-install --skip-buildBest for UI/React development. Uses Vite dev server on port 5174. Changes to React components hot-reload instantly.
cargo xtask notebookWhen testing with multiple notebook windows, closing the first Tauri window normally kills the Vite server. To avoid this:
# Terminal 1: Start Vite standalone (stays running)
cargo xtask vite
# Terminal 2+: Attach Tauri to existing Vite
cargo xtask notebook --attachNow you can close and reopen Tauri windows without losing Vite. This is useful for:
- Testing realtime collaboration
- Testing widgets across windows
- Avoiding confusion when one window close breaks others
Best for:
- Testing Rust changes
- Multiple worktrees (avoids port 5174 conflicts)
- Running the standalone binary
Builds a debug binary with frontend assets bundled in.
# Full build (frontend + rust)
cargo xtask build
# Run the bundled binary
cargo xtask run
# Run with a specific notebook
cargo xtask run path/to/notebook.ipynbcargo xtask build also emits JavaScript source maps for the bundled debug UI,
including inline maps for the isolated renderer iframe bundle, so native webview
devtools can step through .tsx sources.
When you're only changing Rust code (not the frontend), skip the frontend rebuild:
# First time: full build
cargo xtask build
# Subsequent rebuilds: rust only (much faster)
cargo xtask build --rust-only
cargo xtask runThis is ideal for daemon development — build the frontend once, then iterate on Rust with fast rebuilds.
Mostly handled by CI for preview releases. Use locally only when testing:
- App bundle structure
- File associations
- Icons
The UI must be built before Rust because:
crates/notebookembeds assets fromapps/notebook/dist/via Tauri
The xtask commands handle this automatically. If building manually:
pnpm build # Build notebook UI (isolated-renderer built inline)
cargo build # Build RustNote: If you've changed
crates/runtimed-wasm/, rebuild it explicitly withcargo xtask wasm(or the equivalentwasm-pack build ...command) beforepnpm build. Normalcargo xtask buildonly verifies that the committed WASM artifact exists; it does not regenerate it for you.
Test notebooks live in crates/notebook/fixtures/audit-test/ and sample notebooks in crates/notebook/resources/sample-notebooks/.
cargo xtask build
./target/debug/notebook crates/notebook/fixtures/audit-test/1-vanilla.ipynbThe notebook app connects to a background daemon (runtimed) that manages prewarmed environments and notebook document sync. Important: The daemon is a separate process. When you change code in crates/runtimed/, the running daemon still uses the old binary until you reinstall it.
In production, the Tauri app auto-installs and manages the system daemon. In development, you control the daemon yourself, which gives you:
- Isolated state per worktree (no conflicts when testing across branches)
- Your code changes take effect immediately on daemon restart
- No interference with the system daemon
With nteract-dev (preferred for agents):
If you have the repo-local nteract-dev MCP entry configured, the daemon is managed for you:
up— idempotent "get me to a working state". Sweeps zombie Vite processes, ensures daemon is running, ensures the MCP child is healthy. Args:vite=truealso starts Vite (health-probed),rebuild=truerebuilds the daemon binary + Python bindings first,mode="debug"|"release"switches build mode.down— stop the managed Vite dev server. Passdaemon=trueto also stop the daemon.status— read-only report (child, daemon, managed processes, build mode, version).logs— tail daemon logs.vite_logs— tail the Vite dev server log file.
No env vars or extra terminals needed. nteract-dev handles per-worktree isolation automatically.
Two-terminal workflow (without nteract-dev):
# Terminal 1: Start the dev daemon (stays running)
cargo xtask dev-daemon
# Terminal 2: Run the notebook app
cargo xtask notebook # Hot-reload mode
# or
cargo xtask dev # One-shot setup + daemon + app
# or
cargo xtask build # Full build once
cargo xtask build --rust-only && cargo xtask run # Fast iterationThe app detects dev mode and connects to the per-worktree daemon instead of installing/starting the system daemon.
Worktree isolation: When using cargo xtask dev, cargo xtask notebook,
cargo xtask dev-daemon, or cargo xtask run-mcp, xtask automatically enables
dev mode and passes the current git worktree as RUNTIMED_WORKSPACE_PATH to the
subprocesses it starts. Conductor users get the same behavior via
CONDUCTOR_WORKSPACE_PATH.
Non-Conductor users: No extra environment is needed for xtask commands:
# Terminal 1
cargo xtask dev-daemon
# Terminal 2
cargo xtask notebookUseful commands:
./target/debug/runt daemon status # Shows dev mode, worktree path, version
./target/debug/runt dev worktrees # List all running dev daemons (requires RUNTIMED_DEV=1)
./target/debug/runt daemon logs -f # Tail logs (uses correct log path in dev mode)Per-worktree state is stored in <cache>/{cache_namespace}/worktrees/{hash}/ (macOS: ~/Library/Caches/, Linux: ~/.cache/). Source builds default to runt-nightly; set RUNT_BUILD_CHANNEL=stable only when you intentionally need the stable flow.
For AI agents: If the repo-local nteract-dev MCP entry is available, prefer its up / down / status / logs / vite_logs tools — they handle env vars and daemon lifecycle automatically. Keep nteract as the name for the global/system-installed MCP server. When using raw ./target/debug/runt commands outside an xtask-managed subprocess, set the env vars manually:
export RUNTIMED_DEV=1
export RUNTIMED_WORKSPACE_PATH="$(pwd)"
./target/debug/runt daemon statusWhen you need to test the full production flow (daemon auto-install, upgrades, etc.):
# Make sure dev mode is NOT set
unset RUNTIMED_DEV
unset RUNTIMED_WORKSPACE_PATH
# On macOS: install the nteract Nightly .app and let SMAppService manage the daemon.
# On Linux / headless: rebuild and reinstall the full nightly stack from source:
./scripts/install-nightly
# Run the app (it will connect to the system daemon)
cargo xtask notebookNote: ./scripts/install-nightly refuses on macOS by default (the app bundle manages the daemon itself). Pass --on-macos if you really need to install out-of-bundle binaries, and --replace-installed-app if an app bundle is already present.
# View recent logs
runt daemon logs -n 100
# Watch logs in real-time
runt daemon logs -f
# Filter for specific topics
runt daemon logs -f | grep -i "kernel\|auto-detect"If your daemon code changes aren't taking effect:
- In dev mode: Did you restart
cargo xtask dev-daemon? - In production mode (macOS): Re-install the nteract Nightly .app (it auto-updates the bundled daemon).
- In production mode (Linux / headless): Did you run
./scripts/install-nightly? - Check which daemon is running:
runt daemon status
If the app says "Dev daemon not running":
- You're in dev mode but haven't started the dev daemon
- Run
cargo xtask dev-daemonin another terminal first
See contributing/runtimed.md for full daemon development docs.
The nteract MCP server lets AI agents (Claude, Zed, etc.) interact with notebooks via the daemon. There are two ways to run it locally.
The Python workspace root is the repo root — pyproject.toml and .venv
both live there, not inside python/.
| Venv | Location | Purpose |
|---|---|---|
| Workspace venv | .venv (repo root) |
MCP server, uv run nteract, maturin develop target |
| Test venv | python/runtimed/.venv |
Isolated pytest runs for the runtimed bindings |
Sync the workspace (installs nteract + runtimed + deps into .venv):
uv sync # from repo rootBuild the native runtimed extension (from crates/runtimed-py):
cd crates/runtimed-py
VIRTUAL_ENV=../../.venv maturin developThe VIRTUAL_ENV override ensures the .so is installed into the workspace
venv (where the MCP server runs), not into python/runtimed/.venv (the
test-only venv).
Run the MCP server manually:
# Using the built runt binary (preferred)
./target/debug/runt mcp
# Or via the Python wrapper (finds and launches runt mcp)
uv run nteractnteract-dev (the dev MCP server, built from crates/mcp-supervisor/) proxies runt mcp and adds dev tools on top. It handles daemon
lifecycle, auto-restart on crash, and hot-reload on file changes — one command,
everything works:
cargo xtask run-mcpThis:
- Starts the dev daemon if not running
- Builds
runtand spawnsrunt mcpas a child process - Proxies all tool calls + adds the dev tools (
up,down,status,logs,vite_logs) - Watches source files and hot-reloads on changes
For your MCP client config (Zed, Claude Desktop, Codex, etc.):
cargo xtask run-mcp --print-configUse nteract-dev as the server name for this source tree so it stays distinct from any global/system nteract MCP entry.
Codex app/CLI can read a project-scoped .codex/config.toml. This repo includes one that mirrors the same setup:
[mcp_servers.nteract-dev]
command = "cargo"
args = ["run", "-p", "mcp-supervisor"]
cwd = "."
startup_timeout_sec = 120
[mcp_servers.nteract-dev.env]
RUNTIMED_DEV = "1"cwd = "." matters for Codex app/CLI: it forces mcp-supervisor to start from the repo root, which is enough to keep the server pinned to the worktree even when Codex was launched outside a direnv-managed shell. The longer startup timeout avoids dropping nteract-dev during the initial cargo build.
Or configure .zed/settings.json directly (gitignored):
{
"context_servers": {
"nteract-dev": {
"command": "./target/debug/mcp-supervisor",
"args": [],
"env": { "RUNTIMED_DEV": "1" }
}
}
}In clients that namespace tools by server name, this keeps repo-local notebook tools separate from the global install while still exposing the same notebook APIs plus the extra dev tools.
These tools are always available, even when the child runt mcp is down:
| Tool | Purpose |
|---|---|
up |
Idempotent dev-env bring-up. Sweeps zombie Vite processes, ensures daemon + child healthy. Args: vite=true, rebuild=true, mode="debug"|"release" |
down |
Stop the managed Vite dev server. daemon=true also stops the daemon. |
status |
Read-only report: child, daemon, managed processes, build mode, version. |
logs |
Tail the daemon log file. |
vite_logs |
Tail the Vite dev server log file. |
nteract-dev watches crates/runt-mcp/src/, crates/runtimed-client/src/,
python/nteract/src/, python/runtimed/src/, crates/runtimed-py/src/, and
crates/runtimed/src/:
crates/runt-mcp/src/→cargo build -p runt, then child restartcrates/runtimed-client/src/→cargo build -p runt+maturin develop, then child restart- Python changes → child restarts automatically
- Daemon / bindings Rust changes →
maturin developruns first, then child restarts - Behavior changes take effect immediately on the next tool call
- New/removed tools may take a moment for the client to discover
If you don't need auto-restart or file watching, run runt mcp
directly from the dev build:
# Terminal 1: start the dev daemon
cargo xtask dev-daemon
# Terminal 2: launch MCP server (Rust-native, no Python)
./target/debug/runt mcpnteract-dev is the dev-only MCP server for this source tree. It
exposes the dev tools (up, down, status, logs, vite_logs)
itself, then proxies the regular notebook tools from a child runt mcp
process. That child is the Rust-native MCP implementation from
crates/runt-mcp/, not a Python MCP server.
The Python workspace packages still matter for local development:
python/runtimed/ provides the PyO3 bindings, and python/nteract/ is a
convenience wrapper that finds and launches runt mcp (still shipped, not
the recommended path). Both are workspace members of the repo-root
pyproject.toml, so uv sync installs them into .venv at the repo root.
CI rejects PRs that fail formatting. Run this before every commit:
cargo xtask lint --fixThis formats Rust, lints/formats TypeScript/JavaScript with vp (Vite+ unified toolchain), and lints/formats Python with ruff.
Use conventional commits for commit messages and PR titles:
feat(kernel): add environment source labels
fix(runtimed): handle missing daemon socket
docs(agents): enforce conventional commit format
The repo includes .zed/tasks.json with pre-configured tasks. xtask-managed
tasks derive the correct worktree environment automatically. Use task: spawn
(cmd-shift-t) to run them:
| Task | What it does |
|---|---|
| Dev Daemon | cargo xtask dev-daemon; xtask derives the worktree env |
| Dev App | cargo xtask notebook; xtask derives the worktree env and auto-assigned Vite port |
| Daemon Status | ./target/debug/runt daemon status pointed at the worktree daemon |
| Daemon Logs | ./target/debug/runt daemon logs -f with live tail |
| Format | cargo xtask lint --fix (Rust + JS/TS via vp + Python ruff) |
| Setup | pnpm install && cargo xtask build for first-time setup |
The tasks use $ZED_WORKTREE_ROOT for RUNTIMED_WORKSPACE_PATH, giving each Zed worktree its own isolated daemon — no conflicts when working across branches.
For agents in Zed: The Zed task env vars aren't available in agent terminal sessions. Set them explicitly:
export RUNTIMED_DEV=1
export RUNTIMED_WORKSPACE_PATH="/path/to/your/worktree"