@datalabrotterdam/nova-ai-cli
Agentic coding with DataLab Rotterdam's Nova AI: in your terminal, in scripts, and in your editor over the Agent Client Protocol
nova-ai is a coding agent in the spirit of claude and codex, backed by
Nova AI. It reads and edits your
code, runs commands with your permission, asks when a decision is yours, and
keeps a task list for longer work. One agent, three front ends:
- Terminal UI:
nova-ai - Headless:
nova-ai -p "…"for scripts, CI and machines without a GUI - ACP agent:
nova-ai --acpfor Zed, JetBrains, VS Code and any other Agent Client Protocol client
The terminal UI and headless mode talk to the agent over the same protocol an editor uses, so all three behave the same: same tools, permissions, sessions, memory, MCP servers and settings.
Install
You need a Nova AI API key (get one), and for npm Node.js 22.19 or newer. Without Node.js, use a standalone binary.
npm install -g @datalabrotterdam/nova-ai-cli
nova-ai login # opens a local page to enter the key; --no-browser asks in the terminal
nova-ai # start the terminal UI in your project
Or without installing: npx @datalabrotterdam/nova-ai-cli login, then
npx @datalabrotterdam/nova-ai-cli.
When a newer version is on npm, nova-ai asks before the terminal UI opens:
Enter installs it (npm install -g) and restarts, n asks again tomorrow, s
skips that version. Until it is installed, the status line keeps reminding you,
and /update installs it and restarts in the same session. -p and
--version print a notice (on stderr, only in a terminal). npm is asked at
most once a day; an alpha install also follows the alpha channel.
NOVA_NO_UPDATE_CHECK=1 turns it off (it is off in CI).
nova-ai installs updates itself only when it was installed with
npm install -g; with npx, another package manager or a project dependency it
shows the command to run instead.
Standalone binary
No Node.js needed: each GitHub release of the CLI has a single executable with its runtime and all dependencies inside.
macOS and Linux:
curl -fsSL https://datalabrotterdam.nl/nova-ai/install.sh | bash
nova-ai login
Windows PowerShell: irm https://datalabrotterdam.nl/nova-ai/install.ps1 | iex.
Windows cmd: curl -fsSL https://datalabrotterdam.nl/nova-ai/install.cmd -o install.cmd && install.cmd && del install.cmd.
The scripts pick the build for your machine, check it against SHA256SUMS and
install it without admin rights (~/.local/bin, or
%LOCALAPPDATA%\Programs\nova-ai). Run them again to update: they find the
nova-ai already on your PATH, replace it, or say it is current. Options:
--channel alpha (-Channel alpha in PowerShell), --version <v>, --dir <folder>, --force. Until a stable release has binaries, use the alpha channel.
With a package manager, once a stable release is out (how it is published):
brew install --cask datalab-rotterdam/tap/nova-ai # macOS
winget install DatalabRotterdam.NovaAI # Windows
Or download an archive yourself, and check it with
shasum -a 256 --ignore-missing -c SHA256SUMS:
| x64 | Arm64 | |
|---|---|---|
| macOS | nova-ai-darwin-x64.tar.gz | nova-ai-darwin-arm64.tar.gz |
| Linux (glibc) | nova-ai-linux-x64.tar.gz | nova-ai-linux-arm64.tar.gz |
| Windows | nova-ai-windows-x64.zip | nova-ai-windows-arm64.zip |
Apple Silicon Macs (M1 and newer) need arm64. The binaries are not notarized
yet, so a file downloaded with a browser is blocked on macOS and Windows; see the
FAQ. The install script is not affected.
A binary works like an npm install, with two differences:
- Updates come from GitHub releases: the prompt and
/updatedownload the new version, check it againstSHA256SUMS, confirm it starts, and replace the binary and restart. This needs write access to the binary's folder; without it you get a link to the release.SHA256SUMScomes from the same release, so it detects a damaged download, not a tampered release. - The browser integration needs no Node.js: the binary carries the Nova AI
Browser host itself, and the browser starts it as
nova-ai browser-host. Keep the binary where it is, or start it once after moving it, so the browser finds it again.
nova-ai [options] Interactive terminal UI
nova-ai -p [options] [prompt] Run one request headless (see nova-ai -p --help)
nova-ai --acp Agent Client Protocol over stdio (for editors)
nova-ai login [--no-browser] Connect your Nova AI account
nova-ai logout Remove the stored API key
Interactive options:
--resume <session-id> Continue a saved session
-c, --continue Continue the most recent session in this folder
--model <id> Use this model
Terminal UI
The UI runs full screen (the terminal's alternate screen, like less or
vim) and gives your shell its screen back on exit. The mouse wheel or
PgUp/PgDn scroll the transcript, Esc jumps back to the newest output, and
while you read an answer the prompt that asked for it stays pinned on the top
row. Drag over text to select it, then click (or press Enter) to copy it;
Shift+arrows extend the selection and Esc cancels. /copy copies the last
answer.
- Modes (Shift+Tab):
agentdoes the work;planreads the code, asks questions and builds a task list but changes nothing;askonly answers;autopilotworks on until the task is done, picking defaults instead of asking (approvals still ask you). Your own agents (~/.nova-ai/agents/<name>.agent.md, the format the VS Code extension uses) are extra modes after these;/modelists them. - Permissions: before an edit or command you choose once, for this session, always (saved as an exact rule) or deny. Ctrl+K switches between asking, accepting edits, and bypass (never remembered).
- Questions and tasks: the agent can ask 1–4 multiple-choice questions (with descriptions, a recommended option and your own answer) and shows a live checklist for multi-step work.
- Prompt: history,
@filementions,/commandcompletion, large pastes as compact markers, Alt+V to paste an image (for models that accept images). Messages typed while Nova works are queued; ↑ edits them./steer <text>reaches the running turn at the next tool boundary.
| Key | Action |
|---|---|
| Enter / Shift+Enter | Send / new line (Shift+Enter where the terminal supports the kitty keyboard protocol: VS Code, kitty, WezTerm, Ghostty, foot, Alacritty, recent iTerm2; Alt+Enter, Ctrl+J or a trailing \ work everywhere) |
| Esc | Interrupt the running turn (asks whether running background tasks keep going) |
| Ctrl+C | Clear the prompt and cancel the active turn; press again within two seconds to exit (prints nova-ai --resume <id>) |
| Shift+Tab | Cycle agent / ask / plan / autopilot and your own agents |
| Ctrl+R / Ctrl+P / Ctrl+K | Sessions / models / permission mode |
| Ctrl+O / Ctrl+T | Thinking and verbose tool output / tool inspector |
| Alt+V | Paste a clipboard image |
Slash commands: /help, /model, /effort (thinking models), /mode, /permission, /session,
/resume, /clear, /compact, /copy, /rewind [n], /queue, /steer, /usage (context, and the session's tokens and estimated cost; the status line shows ≈ €0.0123 once a priced model answered),
/tools, /tasks (watch or stop background shells and agents), /mcp, /skills, /shell and /agent (background jobs), /trust
and /exit. Your own commands are markdown files in ~/.nova-ai/commands/ or
the repository's .nova-ai/commands/ (Claude Code's format, so
.claude/commands works too): the file is the prompt, $ARGUMENTS what you
type after the command, and they show up in the / menu, together with your
skills (/<skill-name> <request>) and your MCP servers' prompts
(/mcp__<server>__<prompt>). Details:
src/tui/README.md.
Headless
nova-ai -p "review the changed files"
git diff | nova-ai -p --output-format json "summarize this diff"
nova-ai -p --json --permission-mode accept-edits "fix the failing test"
nova-ai -p -c "and now add a changelog entry"
| Option | |
|---|---|
--output-format text|json|stream-json | text prints the answer; json one object at the end (text, tools, permissions); stream-json (or --json) JSON Lines while it runs: session.started, message.delta, tool.started/tool.updated/tool.finished, permission, queue.changed, background.update, result, error |
--permission-mode | read-only (default): anything that needs approval is refused, since nobody can approve it. accept-edits: file edits are accepted, commands still refused. bypass-all: everything is accepted. Deny rules always win. |
--resume <id>, -c | Continue a session |
--model, --cwd | Model and workspace |
--trust-workspace | Trust the workspace (remembered): start the MCP servers and apply the allow rules it declares |
--no-mcp | Ignore workspace MCP configuration |
Exit codes: 0 success, 1 configuration or runtime error, 2 cancelled,
3 tool-turn safety limit, 130 interrupted.
In CI: a GitHub Action (uses: Datalab-Rotterdam/nova-ai-cli@main) and a GitLab
template review pull requests and post the answer as a comment; see
docs/CI.md.
Editors (ACP)
Configure nova-ai --acp as a custom agent. For Zed:
{
"agent_servers": {
"Nova AI": { "command": "npx", "args": ["-y", "@datalabrotterdam/nova-ai-cli", "--acp"] }
}
}
The agent supports session load/list/resume/fork/close/delete, modes, a
permission-mode and model selector, permission requests with once / session /
always options, client file system and terminals, form questions, plans, usage
and title updates, MCP servers (stdio, HTTP, SSE), image prompts, and three
login methods (browser, NOVA_API_KEY, or nova-ai login in a terminal).
Nova-specific features (prompt queue, rewind, compaction, context usage,
per-session settings, background jobs) are _nova/… extension methods. The agent is also published
on its own as
@datalabrotterdam/nova-ai-agent
(command nova-ai-agent), without the terminal UI, for integrations that only
need ACP. Everything is documented in
docs/ACP.md.
Security model
- The agent enforces permissions, not the client. Every front end and every editor gets the same policy; a client can only answer the questions it is asked. Anything other than an explicit "allow" counts as "deny".
- Deny rules win, also in bypass mode and for read-only tools.
- A repository can't approve itself. Allow rules in a workspace's
.nova-ai/settings*.json, and MCP servers it declares (.mcp.json), only take effect after you trust that workspace (asked once at startup,/trust, or--trust-workspace). The trust decision is stored in~/.nova-ai, never in the repository. A repository can't set the permission mode either. - "Always allow" saves an exact rule for that command or path in your
private
~/.nova-ai/projects/<key>/settings.json, outside the repository. Compound commands (a && b, pipes) are checked part by part; substitutions and nested shells need an exact rule. - Web pages are fetched only with approval.
fetch_urlasks every time unless a rule allows that URL (a URL can carry data out), and a redirect to another site needs its own approval.web_searchasks the same way, since the query goes to the search providers (You.com, DuckDuckGo; your own SearXNG server first when~/.nova-ai/settings.jsonhas"webSearch": { "searxngUrl": "https://…" }). - File access stays in the workspace (symlinks resolved); commands run in their own process group with a timeout, and the whole tree is stopped on cancel.
- Credentials are stored in
~/.nova-ai/credentials.jsonwith mode 0600. The browser login page only accepts a one-time token from the same origin.
Settings, memory and sessions
Everything lives in ~/.nova-ai (or $NOVA_AI_HOME), shared with the Nova
VS Code extension: settings, per-project trust, sessions and memory.
- Rules:
{"permissions": {"allow": ["run_command(npm test)", "edit_file(src/*)"], "deny": ["read_file(.env)"]}}in~/.nova-ai/settings.json, the private project file, or the workspace's.nova-ai/settings.json(team, committed) and.nova-ai/settings.local.json(personal). Claude-style aliases such asBash(npm run *)andEdit(src/**)work too. - Instructions:
NOVA.mdandAGENTS.mdin the workspace. - Memory: a
MEMORY.mdindex plus notes, globally and per project, through thememory_readandmemory_writetools (writes need approval). - Skills:
SKILL.mdfiles under.nova-ai/skillsin the workspace or~/.nova-ai/skillsfor every project (.agents,.claudeand.codexskills folders are read too)./skillslists them;/skills off <name>switches one off in this project (--global: everywhere). Only names and short descriptions go into the prompt; Nova loads a skill when it fits. Nova installs its own skills for Word, Excel, PowerPoint and PDF files (nova-docx,nova-xlsx,nova-pptx,nova-pdf, from nova-ai-skills) in~/.nova-ai/skillsand keeps them up to date; they need Python 3. - Hooks: Claude Code's
hooksformat in the same settings files (a repository's own only once you trust the workspace):PreToolUse,PostToolUse,UserPromptSubmit,SessionStartandStop, with Claude Code's tool names as matchers, so hooks from.claude/settings.jsonwork when copied over. - MCP servers:
.mcp.jsonormcpServersin.nova-ai/settings.json. HTTP servers that use OAuth show "needs sign-in" in/mcp;/mcp login <server>opens the sign-in page in your browser (tokens are kept in~/.nova-ai/mcp-auth, refreshed automatically),/mcp logout <server>forgets them. A server that wants a client registered by hand takes"oauth": { "clientId", "clientSecret", "scope", "callbackPort" }in.mcp.json.
The complete layout and rule syntax: docs/NOVA_HOME.md.
Models
The model list shows the chat models of your account. On login Nova picks the
first one with native tool calling; /model or Ctrl+P changes it (and
remembers the choice), --model or NOVA_MODEL sets it for one run. Nova uses the gateway's function calling and falls back
to a text protocol for models that don't support it. When the context window
fills up, older messages are summarized automatically (/compact does it by
hand).
Thinking models are marked ◆ in the model list. For those with levels the
status line shows the current one (· thinking medium) and /effort opens a
slider (←/→, Enter), or sets one directly (/effort high, /effort off). The
choice is remembered like the model; a model without that level uses its own
default.
| Environment | |
|---|---|
NOVA_API_KEY, NOVA_MODEL | Use this key / model instead of the stored ones |
NOVA_BASE_URL | Another Nova (OpenAI-compatible) gateway |
NOVA_AI_HOME | Settings folder (default ~/.nova-ai) |
NOVA_TOOL_PROTOCOL | Force native or text tool calling |
NOVA_NES_MODEL | Separate model for next-edit suggestions (ACP) |
NOVA_KITTY_KEYBOARD | 1/0: force the kitty keyboard protocol on or off |
NOVA_NO_UPDATE_CHECK | Don't ask npm for a newer version (also NO_UPDATE_NOTIFIER, or CI) |
FAQ
macOS won't open nova-ai
Binaries downloaded with a browser are quarantined, and they aren't notarized by Apple yet. You'll see one of these:
- "nova-ai" is damaged and can't be opened
- Apple could not verify "nova-ai" is free of malware
zsh: killed ./nova-aiin the terminal
The simplest fix is the install script, because curl doesn't
quarantine files. To keep a browser download, cancel the dialog (don't move the
file to the Trash) and clear the quarantine flag in the binary's folder:
xattr -d com.apple.quarantine nova-ai
./nova-ai
Do this only for a file from the
releases page that
matches SHA256SUMS. sudo doesn't help: macOS blocks the binary for root too.
On an Apple Silicon Mac, also check that you have darwin-arm64, not darwin-x64.
Windows SmartScreen blocks nova-ai
Choose More info → Run anyway. The binaries aren't code-signed yet.
nova-ai: command not found after installing
~/.local/bin isn't on your PATH. Add export PATH="$HOME/.local/bin:$PATH" to
~/.zshrc or ~/.bashrc and open a new terminal.
"The update did not install" or no /update
A binary replaces itself, so it needs write access to its own folder. Installed
with the script, it has that. Otherwise nova-ai shows a link to the release.
Development
This package lives in the nova-ai-cli repository next to the agent package it depends on; see the repository README for building and testing.
License
Apache-2.0 © DataLab Rotterdam