REPL interface pattern
readline-based, works for any CLI tool
Problem
Building a CLI tool that needs to accept continuous user input — a chatbot, a debugger shell, an interactive query interface — requires managing the input/output loop, prompt rendering, signal handling (Ctrl-C, Ctrl-D), and graceful shutdown. Reimplementing these mechanics from scratch in each project is error-prone and produces inconsistent behavior across tools. Eliza needed a reliable, minimal interactive shell for the terminal-based conversation interface.
Solution
Use Node.js’s built-in readline module to create a read-eval-print loop. Configure a readline.Interface with process.stdin and process.stdout, define a prompt string, and listen for the line event to process each user input. Handle close for graceful exit on EOF (Ctrl-D) and optionally listen for SIGINT (Ctrl-C) to exit cleanly.
import * as readline from "readline";
function startREPL(handler: (input: string) => string): void {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: "> ",
});
rl.prompt();
rl.on("line", (line) => {
const input = line.trim();
if (!input) {
rl.prompt();
return;
}
const response = handler(input);
console.log(response);
rl.prompt();
});
rl.on("close", () => {
console.log("\nGoodbye.");
process.exit(0);
});
}The handler function decouples the REPL mechanics from the application logic. Any function that takes a string and returns a string can be plugged in.
Usage
Apply for any Node.js CLI tool that requires interactive, stateful user input. Covers:
- Conversational agents (Eliza-style or LLM-backed)
- Interactive debugger shells
- Query interfaces for local data stores
- Development tools requiring repeated test inputs
The pattern uses zero external dependencies — Node.js’s readline is part of the standard library. For richer features (history, autocompletion, multi-line input), consider readline extensions or the enquirer package, but only introduce them if the basic pattern proves insufficient.
Trade-offs
- Pro: Zero dependencies. Works in any Node.js environment without installation.
- Pro: The handler abstraction keeps application logic completely independent of I/O mechanics — trivial to test the handler in isolation.
- Con:
readlinedoes not support multi-line input or complex keybinding customization without significant additional work. - Con: Output written to
process.stdoutduring async handler execution can interleave with the prompt display, producing garbled output. Async handlers require careful prompt re-display after awaiting responses.
See Also
Source
Synced from ops-core learning data. Original source: Eliza