A unified CLI and web dashboard for running and managing Jupyter, Marimo, and Pluto notebooks.
everything is vibe-coded including this README, No Quality Assurance
- One tool for Jupyter, Marimo, and Pluto — engine picked from the file extension
- Organize notebooks into workspaces
- Notebook explorer — browse notebooks across workspaces, preview their code cells without running them, and double-click any notebook to start it
- Per-notebook runtime memory — nbm remembers which Python/Julia executable each notebook was created with, so you don't have to
cdinto the right project or activate the right env every time.nbm start your_notebook.pyis enough. - Embedded mode — run multiple notebooks inside one nbm UI and switch between them — or standalone mode, where each notebook runs on its own as usual
- Tracks running sessions; clean start/stop/remove
npm install -g @afterpython/nbmSupported platforms: macOS arm64, macOS x64, Linux x64.
External requirements (one or more, depending on which notebook engines you use):
pythonwithmarimo(for.py) and/orjupyter(for.ipynb)juliawithPluto(for.jl)
nbm finds these on PATH at runtime; it doesn't bundle them.
nbm ui # start the web UI
nbm start notebook.py # start a Marimo notebook
nbm start notebook.jl --workspace research # start a Pluto notebook in the "research" workspace
nbm start notebook.ipynb -w experiment # start a Jupyter notebook in the "experiment" workspaceThe notebook engine is chosen from the file extension:
| Extension | Engine |
|---|---|
.py |
Marimo |
.ipynb |
Jupyter |
.jl |
Pluto |
Anything you put after -- on nbm start is forwarded verbatim to the underlying engine (marimo edit / jupyter notebook):
nbm start notebook.py -- --port 8765 # marimo edit ... --port 8765
nbm start notebook.ipynb -- --ServerApp.token='' # jupyter notebook ... --ServerApp.token=''Pluto doesn't take CLI flags this way, so -- is effectively a no-op for .jl notebooks.
nbm has two display modes (set with nbm config set mode <value>):
-
embedded(default) — All running notebooks live inside the nbm UI. Startnbm uifirst in another terminal, then runnbm startfor each notebook; each running notebook gets its own tab in the sidebar, and you can cycle between them withAlt+J/Alt+K. Under the hood each notebook is rendered in its own iframe.Rebind those shortcuts with
nbm config set nextRunningKeybinding <combo>andnbm config set previousRunningKeybinding <combo>— e.g.nbm config set nextRunningKeybinding Ctrl+Shift+J. On macOS,Altis the Option key. -
standalone— Notebooks run as their own normal web apps (just like running Jupyter/Marimo/Pluto by hand).nbm uiis optional and is only useful as a central overview.
If a notebook misbehaves in embedded mode (some notebook frontends don't love being framed), switch to standalone:
nbm config set mode standalone| Command | Description |
|---|---|
nbm ui [-p, --port <port>] |
Start the web dashboard in the foreground (Ctrl+C to stop). Port auto-picks if omitted. |
nbm start <notebook> [-w, --workspace <name>] [--no-open] [-- <extra args>] |
Start (or create + start) a notebook. In embedded mode, nbm ui must already be running. In standalone mode, --no-open suppresses browser opening. Anything after -- is passed through to the notebook runner. |
nbm stop <name|id> [-w, --workspace <name>] |
Stop a running notebook. |
nbm stop all |
Stop every running notebook session. |
nbm stop ui |
Stop the embedded web UI. |
nbm list [-a, --all] [-w, --workspace <name>] [-t, --type <marimo|jupyter|pluto>] (alias ls) |
Table of id / name / type / workspace / status. By default only running notebooks are shown; pass --all to include stopped ones. --workspace and --type filter the list and compose with --all. |
nbm remove <name|id> [-w, --workspace <name>] (alias rm) |
Stop the notebook, delete its registry entry and on-disk files. |
nbm config list | get <key> | set <key> <value> |
View or change configuration. |
nbm runtime set <workspace/name|id> --binary <abs-path> [--project <abs-path>] |
Pin a Python/Julia binary to a notebook. The notebook must be stopped. --project is Pluto-only. |
When you omit -w, the workspace defaults to default.
When you first nbm start a notebook, nbm records the Python (or Julia) executable that's active in your shell at that moment and pins it to the notebook. Every later nbm start for that notebook reuses the same executable — no need to re-activate a venv, cd into the project, or remember which env it belongs to.
To change a notebook's runtime later (for example, to point a Marimo notebook at a different Python env):
nbm runtime set notebook.py --binary /abs/path/to/another/pythonTo target a notebook in a non-default workspace, use the workspace/notebook form (this slash syntax is specific to nbm runtime set — other commands use --workspace instead):
nbm runtime set research/notebook.py --binary /abs/path/to/another/pythonFor Pluto notebooks, you can also pin a Julia project directory:
nbm runtime set notebook.jl --binary /abs/path/to/julia --project /abs/path/to/JuliaProjectNotes:
- Paths must be absolute.
- The notebook must be stopped first (
nbm stop notebook.py). - You can also pass a notebook id instead of a name.
nbm config list| Key | Default | Description |
|---|---|---|
mode |
embedded |
embedded or standalone (see above). |
nextRunningKeybinding |
Alt+J |
Shortcut to switch to the next running notebook in the sidebar. |
previousRunningKeybinding |
Alt+K |
Shortcut to switch to the previous running notebook. |
On macOS, Alt is the Option key.
- Stack: Deno CLI (Cliffy) + SvelteKit web dashboard, pnpm workspace, shared
@nbm/corepackage. - Layout:
apps/cli,apps/web,packages/core. - Requirements: pnpm 10+, Deno.
- Scripts (from package.json):
pnpm dev:web— run the web dashboard in dev modepnpm dev:cli— run the CLI in dev modepnpm check— type-check all packagespnpm lint/pnpm format
Every release is one command — bump the version:
./scripts/release.sh 0.1.1
./scripts/release.sh 0.2.0The script tags and pushes; CI builds the binaries and publishes the four npm packages. See RELEASING.md for architecture and recovery.
Apache-2.0