Content
# node-repl-cli
**Agent skill (portable):** [`skills/node-repl-cli/SKILL.md`](skills/node-repl-cli/SKILL.md) — see [`skills/README.md`](skills/README.md) for the `skills/` layout.
**Named, persistent Node.js REPL sessions for automation and AI agents** — without asking a model to “type into” an interactive shell. Each session is a **real [`node:repl`](https://nodejs.org/api/repl.html) context** in its **own process**, so variables and module state survive across evaluations until you remove the session or restart the machine.
---
## What it is for
- Run **JavaScript as in the Node REPL** (shared context per session), from **scripts**, **CI**, or **LLM tool calls**.
- Give **AI coding agents** a small, predictable surface: **create session → run code → read history → tear down** — no terminal UI, no `stdin` REPL loop.
- Keep **multiple isolated sessions** at once (e.g. `app`, `tests`, `scratch`) without one session polluting another.
---
## How it works (architecture)
```mermaid
flowchart LR
subgraph cli["node-repl-cli (short-lived)"]
C1[create / exec / …]
end
subgraph coord["Coordinator serve"]
M[ReplSessionManager Map]
T[TCP JSON-RPC]
end
subgraph daemons["Per-session daemons"]
D1[REPL + TCP]
D2[REPL + TCP]
end
C1 -->|loopback| T
T --> M
M --> D1
M --> D2
```
1. **Session daemon** — For each session name, a **detached child process** runs `ReplDaemonMain` with **`process.cwd()` set to a user-local workspace** (`…/node-repl-cli/sessions/<name>/`): one `NodeReplSession` (REPL server) plus a **loopback TCP** control plane (`eval`, `history`, `shutdown`). The parent reads a **one-line JSON handshake** from the child’s stdout to learn `port` / `pid`. The **running session table** stays in the coordinator’s memory only; **dependencies** for that name live in that folder (`package.json`, `node_modules`).
2. **Coordinator** — `node-repl-cli serve` holds an in-memory **name → (host, port, pid, workspace)** table and exposes **newline-delimited JSON-RPC** over TCP (`ping`, `create`, `eval`, `history`, `remove`, `list`, `pkgInstall`, `pkgRemove`, `pkgList`). Stateless CLI commands talk to this coordinator.
3. **Coordinator lock file** — After `serve` binds and self-pings, it writes **`coordinator.json`** under the **state directory** (`REPL_CLI_STATE_DIR` or the default OS-specific path; see below) so later CLI invocations can find it without flags. On graceful shutdown (signal or **last session removed**), the lock is cleared.
4. **MCP mode** — `node-repl-cli mcp` embeds the same `ReplSessionManager` **inside the MCP process**: no coordinator TCP, no lock file. Each tool call drives daemons the same way, but the registry lives only for the lifetime of that MCP server.
---
## Features
| Feature | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **True REPL semantics** | `node:repl` per session — `let` / `require` / globals persist across `exec` calls in that session. |
| **Multi-session** | Many named sessions; each has its own process and REPL context. |
| **Agent-friendly CLI** | No interactive shell required; each subcommand is one-shot and scriptable. |
| **Zero-config by default** | Autodiscovery via lock file + **autostart** of `serve` when needed (`REPL_CLI_AUTOSTART=0` to disable). |
| **Per-session npm** | `node-repl-cli pkg <name> install|remove|list` (or coordinator RPC) runs **npm** / reads `package.json` in that session’s workspace so each REPL has its own **`node_modules`**. |
| **Disk layout** | Coordinator lock file **plus** optional `sessions/<name>/` trees for npm; **no** per-session daemon metadata files. |
| **Coordinator auto-exit** | With `serve`, after **`remove`** deletes the **last** session, the coordinator exits and clears the lock (no orphan `serve`). |
| **MCP** | [Model Context Protocol](https://modelcontextprotocol.io/) tools for Cursor, Claude Desktop, etc. |
---
## Install & build
```bash
git clone <repo-url>
cd node-repl-cli
npm install
npm run build
```
Global CLI (after `npm link` or `npm install -g .`):
```bash
node-repl-cli --help
```
Local / CI:
```bash
npx node-repl-cli --help
# or
node dist/cli.js --help
```
---
## Usage (CLI)
Typical flow — **no address flags**; the first command may autostart `serve` in the background:
```bash
node-repl-cli pkg mycli install lodash # optional: before or after create
node-repl-cli pkg mycli list # declared dependencies from package.json
node-repl-cli create mycli
node-repl-cli exec mycli -c "const fs = await import('node:fs'); fs.readFileSync.length"
node-repl-cli exec mycli -c "globalThis.n = (globalThis.n ?? 0) + 1; globalThis.n"
node-repl-cli history mycli # JSON array: input, output, error, captured console streams
node-repl-cli list # JSON array of session records (name, port, workspace, …)
node-repl-cli pkg mycli remove lodash
node-repl-cli remove mycli
```
- Session names: `^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$`.
- **`pkg <name> <action>`** — `action` is `install` (aliases `add`), `remove` (aliases `rm`, `uninstall`), or `list`. Install supports `-D` / `--save-dev`. It uses the same session workspace directory the daemon uses. Removing a session **does not** delete that folder.
### CLI examples (typical stdout and exit codes)
Paths and ports differ per machine; below shows **shape** only.
**`create`** — prints one JSON object (session metadata + `workspace`):
```text
$ node-repl-cli create demo
{"name":"demo","status":"ready","port":58432,"pid":12345,"createdAt":"2026-04-13T12:00:00.000Z","workspace":"/home/you/.local/state/node-repl-cli/sessions/demo"}
```
**`exec` success** — one JSON line per invocation; inspect `error` (should be `null`) and `output`:
```text
$ node-repl-cli exec demo -c "1+1"
{"input":"1+1","output":"2","error":null,"streams":{"stdout":"","stderr":""}}
```
Exit code **0**.
**`exec` runtime error** — `error` is a string (often a stack); CLI sets exit code **1**:
```text
$ node-repl-cli exec demo -c "missingVar"
{"input":"missingVar","output":null,"error":"ReferenceError: missingVar is not defined\n at ...","streams":{"stdout":"","stderr":""}}
```
Exit code **1**.
**`list`** — running sessions only (JSON **array** of session records):
```text
$ node-repl-cli list
[{"name":"demo","status":"ready","port":58432,"pid":12345,"createdAt":"...","workspace":"/home/you/.local/state/node-repl-cli/sessions/demo"}]
```
**`pkg … list`** — reads `package.json` (no npm network):
```text
$ node-repl-cli pkg demo list
{"workspace":"/home/you/.local/state/node-repl-cli/sessions/demo","dependencies":{"lodash":"^4.17.21"},"devDependencies":{}}
```
**`pkg … install`** — npm may print progress to **stderr** / **stdout** first; the CLI then appends a final JSON line:
```text
$ node-repl-cli pkg demo install semver
…npm progress on stderr/stdout…
{"ok":true,"code":0}
```
Non‑zero npm exit → same stderr noise, **no** trailing `{"ok":true,...}`; process exit code ≠ 0.
**`remove`**:
```text
$ node-repl-cli remove demo
{"ok":true}
```
### Environment (optional)
| Variable | Purpose |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `REPL_CLI_STATE_DIR` | **State root directory** (absolute or cwd-relative). Contains `coordinator.json` and `sessions/<name>/`. Overrides the default OS paths below. |
| `REPL_CLI_NPM_CACHE` | If set, passed to npm as `npm_config_cache` for `pkg … install` / `remove` subprocesses (isolated npm cache per project or CI job). |
| `REPL_CLI_AUTOSTART` | Default on. Set to `0` / `false` / `off` / `no` to **never** spawn `serve` automatically; you must run `node-repl-cli serve` yourself. |
| `REPL_CLI_ADDR` | Force `host:port` of the coordinator (CI, multiple coordinators, or debugging). |
### Manual coordinator
```bash
node-repl-cli serve
```
Default **`--port 0`** (OS-assigned). Use a fixed `--port` if you pin `REPL_CLI_ADDR`.
### State directory and lock file (default layout)
Unless **`REPL_CLI_STATE_DIR`** is set, the state root is:
- **Windows:** `%LOCALAPPDATA%\node-repl-cli\` — holds `coordinator.json` and `sessions\`
- **Linux / macOS (XDG):** `$XDG_STATE_HOME/node-repl-cli/`, or `~/.local/state/node-repl-cli/` if `XDG_STATE_HOME` is unset
With **`REPL_CLI_STATE_DIR=/path/to/foo`**, paths become `/path/to/foo/coordinator.json` and `/path/to/foo/sessions/<name>/`.
---
## For AI agents
Agents should **not** drive an interactive REPL. Use one of these patterns.
Portable instructions for any skill-capable host: **[`skills/node-repl-cli/SKILL.md`](skills/node-repl-cli/SKILL.md)** (see **[`skills/README.md`](skills/README.md)** for the layout).
### 1. MCP (recommended in Cursor / MCP hosts)
Run the server once in the host config (stdio):
```bash
node-repl-cli mcp
```
Register tools (arguments are JSON; responses are **text** bodies containing JSON):
| Tool | Arguments (JSON) | Typical use |
| -------------------------- | ------------------------------------ | ------------------------------------ |
| `repl_cli_ping` | — | Health check |
| `repl_cli_session_create` | `{ "name": "mycli" }` | New session + daemon |
| `repl_cli_session_exec` | `{ "name": "mycli", "code": "..." }` | Run code in that session’s REPL |
| `repl_cli_session_history` | `{ "name": "mycli" }` | Returns `{ "ok", "entries": [...] }` |
| `repl_cli_session_remove` | `{ "name": "mycli" }` | Stop daemon + drop name |
| `repl_cli_pkg_install` | `{ "name": "mycli", "packages": ["lodash"], "saveDev": false }` | `npm install` in that session’s workspace |
| `repl_cli_pkg_remove` | `{ "name": "mycli", "packages": ["lodash"] }` | `npm uninstall` in that workspace |
| `repl_cli_pkg_list` | `{ "name": "mycli" }` | Declared `dependencies` / `devDependencies` from that workspace `package.json` |
**OpenAPI (logical MCP tool API):** [`openapi/node-repl-cli-mcp.yaml`](openapi/node-repl-cli-mcp.yaml) — OpenAPI 3.0 schemas and examples for every tool; transport remains MCP stdio, not the placeholder `servers.url`.
**Important:** MCP mode keeps sessions **only inside that MCP process**. It does **not** share the coordinator used by standalone `node-repl-cli create` unless you design that yourself.
**Suggested agent loop:** optional **`repl_cli_pkg_install`** (or CLI `pkg <name> install …`) → `create` → repeated `exec` → optional `history` → **`repl_cli_pkg_list`** when you need declared deps → `remove` when done (workspace + `node_modules` remain on disk unless you delete the folder manually).
### 2. Subprocess CLI (agents that only have `run_terminal_cmd`)
Use the same commands a human would; each invocation is stateless from the agent’s perspective, while state lives in the coordinator + daemons. See **[CLI examples](#cli-examples-typical-stdout-and-exit-codes)** (under Usage) for command → JSON stdout shapes, npm interleaved output, and exit codes.
```bash
node-repl-cli exec mycli -c "code"
```
Rely on **autostart** + lock file so you do not pass `--addr`. If the sandbox cannot spawn background processes, set `REPL_CLI_AUTOSTART=0` and start `node-repl-cli serve` in a long-lived sidecar first.
### 3. Custom clients
The coordinator speaks **one JSON object per line** over TCP on the loopback address from the lock file:
- **Request:** `{ "id": <any>, "method": "ping"|"create"|"eval"|"history"|"remove"|"list"|"pkgInstall"|"pkgRemove"|"pkgList", "params": { ... } }`
- `list`: `{}` → JSON **array** `[ { "name", "status", "port", "pid", "createdAt", "workspace" }, ... ]`
- `pkgInstall`: `{ "name": "<session>", "packages": ["<spec>", ...], "saveDev"?: boolean }` → `{ code, stdout, stderr }`
- `pkgRemove`: `{ "name": "<session>", "packages": ["<name>", ...] }` → `{ code, stdout, stderr }`
- `pkgList`: `{ "name": "<session>" }` → `{ workspace, dependencies, devDependencies }`
- **Response:** `{ "id": <same>, "ok": true, "result": ... }` or `{ "ok": false, "error": "..." }`
Session daemons use a similar line protocol (`ping`, `eval`, `history`, `shutdown`).
---
## Security
- Listeners are bound to **127.0.0.1** only.
- User code runs with **full Node privileges** in session daemons (same as `node`). Only use with **trusted** code and environments.
- **`npm install` / `npm uninstall`** run normal **npm lifecycle scripts** with your user permissions; treat package specs like any shell command input.
- **npm subprocess:** when Node ships the bundled **`npm-cli.js`** next to `node.exe`, this tool runs **`node …/npm-cli.js <args>`** (no `cmd.exe` shell, **`windowsHide: true`** on Windows) so package installs work even from a detached `serve` parent and normally **without** a flashing console. If that file is missing (unusual installs), it falls back to **`npm`** with `shell: true` on Windows only. After upgrading the package, **restart `serve`** (or let autostart spawn a fresh coordinator) so the coordinator loads the new code.
---
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.