Skip to content

mycrl/node-repl-cli

Repository files navigation

node-repl-cli

Agent skill (portable): skills/node-repl-cli/SKILL.md — see 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 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)

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
Loading
  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. Coordinatornode-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 modenode-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 install
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 tools for Cursor, Claude Desktop, etc.

Install & build

git clone <repo-url>
cd node-repl-cli
npm install
npm run build

Global CLI (after npm link or npm install -g .):

node-repl-cli --help

Local / CI:

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:

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):

$ 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:

$ node-repl-cli exec demo -c "1+1"
{"input":"1+1","output":"2","error":null,"streams":{"stdout":"","stderr":""}}

Exit code 0.

exec runtime errorerror is a string (often a stack); CLI sets exit code 1:

$ 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):

$ 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):

$ 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:

$ 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:

$ 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

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 (see skills/README.md for the layout).

1. MCP (recommended in Cursor / MCP hosts)

Run the server once in the host config (stdio):

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 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 historyrepl_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 (under Usage) for command → JSON stdout shapes, npm interleaved output, and exit codes.

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

About

Persistent Node.js REPL sessions (real node:repl), CLI + MCP

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors