@datalabrotterdam/nova-ai-cli
v1.1.0
Published
CLI for DataLab Rotterdam's Nova AI, with an Agent Client Protocol mode for editors/IDEs, backed by @datalabrotterdam/nova-sdk
Downloads
359
Readme
@datalabrotterdam/nova-ai-cli
CLI for DataLab Rotterdam's Nova AI — also speaks Agent Client Protocol
Talk to DataLab Rotterdam's Nova AI from your terminal, or point your editor/IDE at it via Agent Client Protocol — both modes share the same auth and run on @datalabrotterdam/nova-sdk.
⚠️ Work in progress. ACP mode (
--acp) is the only fully implemented mode today — agentic tool use, permissions, and session persistence all work there. Interactive terminal chat (no flags) and the standalone browser UI (--web) are stubs that print a "not built yet" message. Expect breaking changes before a 1.0.
Installation
No install needed — run directly with npx:
npx @datalabrotterdam/nova-ai-cli --setup # store your Nova API key
npx @datalabrotterdam/nova-ai-cli # interactive chat (WIP, stub)
npx @datalabrotterdam/nova-ai-cli --acp # speak ACP over stdio (editors/IDEs) — fully working
npx @datalabrotterdam/nova-ai-cli --web # standalone browser UI (WIP, stub)Or install globally / as a project dependency:
npm install -g @datalabrotterdam/nova-ai-cliRequires: Node.js >= 18, a Nova AI API key.
Overview
nova-ai-cli is a terminal client for Nova AI, in the same spirit as claude/codex. Pass --acp to run it in Agent Client Protocol mode, where it reads JSON-RPC requests from stdin and writes responses/notifications to stdout so any ACP-aware editor/IDE can drive it like any other coding agent — with tool calling, permission prompts, and persisted session history. Bare/--web modes are placeholders for now. All modes share the same stored credentials.
# 1. one-time setup — opens a browser page to verify the key against Nova AI and store it locally
npx @datalabrotterdam/nova-ai-cli --setup
# 2. point your ACP client at this binary
npx @datalabrotterdam/nova-ai-cli --acpMost editors invoke --acp for you once configured as an agent — --setup is the one command you run by hand first.
Status: --setup and --acp (including tool calling and session persistence) are fully implemented. Interactive terminal chat (no flags) and --web are stubs — see the WIP note above.
Auth
This agent advertises Agent Auth (per AUTHENTICATION.md): when the client calls authenticate, the agent itself spins up a local HTTP server, opens your browser to it, and resolves once you've connected your account.
{
"id": "nova-api-key",
"name": "Nova API Key"
}Today the page served at that local URL is a small Svelte form that:
- Collects your Nova API key.
- Validates it against
client.models.list(). - Picks a default model (first enabled model) and stores
{ apiKey, defaultModel }in~/.nova-ai/credentials.json.
You can also trigger this manually:
npx @datalabrotterdam/nova-ai-cli --setupOverrides, no browser flow required:
| Env var | Purpose |
|---|---|
| NOVA_API_KEY | Skip stored credentials, use this key |
| NOVA_MODEL | Skip stored default model, use this model id |
A future version will swap this for real OAuth once DataLab Rotterdam ships an OAuth provider for Nova AI — same local server (src/acp/auth-server.ts), same lifecycle, just a different page/flow. Credential handling stays isolated in src/acp/credentials.ts either way.
What it implements
| ACP method | Behavior |
|---|---|
| initialize | Returns protocol version + the nova-api-key agent auth method |
| session/new | Allocates a session id with in-memory history |
| session/load | Restores a previously-persisted session and replays its turns as *_message_chunk notifications |
| session/list | Lists stored sessions, optionally filtered by cwd |
| authenticate | Runs the local browser auth flow (src/acp/auth-server.ts) and resolves once a valid key is stored |
| session/prompt | Streams a chat.completions.stream() call, forwarding agent_message_chunk updates as text arrives; drives the tool-calling loop below |
| session/cancel | Aborts the in-flight Nova AI request via AbortController |
Only text and resource_link content blocks are read from prompts today — images/audio/embeddings supported by nova-sdk aren't wired up yet.
Sessions are persisted to disk (src/acp/sessions.ts) so session/load can rehydrate history, and a title is auto-derived from the conversation.
Tool calling
While streaming a response, the agent watches for a fenced tool-call marker in the model's output (src/acp/tools/marker.ts). When one appears, it stops streaming text and instead:
- Reports the pending call via
session/update(tool_callwith statuspending). - Requests permission from the client for any mutating tool (
allow_once/reject_once), viasession/request_permission. - Executes the tool and reports the result/error via a
tool_call_update. - Feeds the tool result back into the conversation and loops (up to 10 rounds per prompt) until the model produces a plain answer.
Built-in tools (src/acp/tools/registry.ts):
| Tool | Mutating | Behavior |
|---|---|---|
| read_file | No | Reads a file's contents, gated by client fs.readTextFile capability |
| write_file | Yes | Writes/overwrites a file, gated by client fs.writeTextFile capability — requires permission |
| run_command | Yes | Runs a shell command in the session's cwd — requires permission |
Tools are filtered per-session by the client's advertised clientCapabilities, so a client without filesystem support never sees read_file/write_file offered.
Development
npm install
npm test # tsc --noEmit + node --test on tests/
npm run build # builds the Svelte setup page, then tsc -> dist/Releases are automated via semantic-release on push to main (see .github/workflows/ci.yml); commit messages drive version bumps.
License
Apache-2.0 © DataLab Rotterdam
