rlm-cli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -15
- package/dist/interactive.js +84 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,34 +13,38 @@ CLI for **Recursive Language Models** — based on the [RLM paper](https://arxiv
|
|
|
13
13
|
|
|
14
14
|
Instead of dumping a huge context into a single LLM call, RLM lets the model write Python code to process it — slicing, chunking, running sub-queries on pieces, and building up an answer across multiple iterations.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g rlm-cli
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Set your API key:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
26
|
+
# or
|
|
27
|
+
export OPENAI_API_KEY=sk-...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. Run `rlm` and you're in.
|
|
31
|
+
|
|
32
|
+
### From Source
|
|
17
33
|
|
|
18
34
|
```bash
|
|
19
35
|
git clone https://github.com/viplismism/rlm-cli.git
|
|
20
36
|
cd rlm-cli
|
|
21
37
|
npm install
|
|
22
38
|
npm run build
|
|
23
|
-
npm link
|
|
39
|
+
npm link
|
|
24
40
|
```
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
Create a `.env` file in the project root with your API key:
|
|
27
43
|
|
|
28
44
|
```bash
|
|
29
45
|
cp .env.example .env
|
|
30
46
|
```
|
|
31
47
|
|
|
32
|
-
```bash
|
|
33
|
-
# .env
|
|
34
|
-
ANTHROPIC_API_KEY=sk-ant-...
|
|
35
|
-
# or
|
|
36
|
-
OPENAI_API_KEY=sk-...
|
|
37
|
-
|
|
38
|
-
# Optional: override default model
|
|
39
|
-
# RLM_MODEL=claude-sonnet-4-5-20250929
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
That's it. Run `rlm` and you're in.
|
|
43
|
-
|
|
44
48
|
## Usage
|
|
45
49
|
|
|
46
50
|
### Interactive Terminal
|
package/dist/interactive.js
CHANGED
|
@@ -153,19 +153,23 @@ function printWelcome() {
|
|
|
153
153
|
function printCommandHelp() {
|
|
154
154
|
console.log(`
|
|
155
155
|
${c.bold}Context${c.reset}
|
|
156
|
-
${c.
|
|
157
|
-
${c.
|
|
158
|
-
${c.
|
|
159
|
-
${c.
|
|
160
|
-
${c.
|
|
156
|
+
${c.cyan}/file${c.reset} <path> Load file as context
|
|
157
|
+
${c.cyan}/url${c.reset} <url> Fetch URL as context
|
|
158
|
+
${c.cyan}/paste${c.reset} Multi-line paste mode (EOF to finish)
|
|
159
|
+
${c.cyan}/context${c.reset} Show loaded context info
|
|
160
|
+
${c.cyan}/clear-context${c.reset} Unload context
|
|
161
|
+
|
|
162
|
+
${c.bold}Model${c.reset}
|
|
163
|
+
${c.cyan}/model${c.reset} List available models
|
|
164
|
+
${c.cyan}/model${c.reset} <#|id> Switch model by number or ID
|
|
161
165
|
|
|
162
166
|
${c.bold}Tools${c.reset}
|
|
163
|
-
${c.
|
|
167
|
+
${c.cyan}/trajectories${c.reset} List saved runs
|
|
164
168
|
|
|
165
169
|
${c.bold}General${c.reset}
|
|
166
|
-
${c.
|
|
167
|
-
${c.
|
|
168
|
-
${c.
|
|
170
|
+
${c.cyan}/clear${c.reset} Clear screen
|
|
171
|
+
${c.cyan}/help${c.reset} Show this help
|
|
172
|
+
${c.cyan}/quit${c.reset} Exit
|
|
169
173
|
|
|
170
174
|
${c.dim}Or just paste a URL or 4+ lines of code, then type your query.${c.reset}
|
|
171
175
|
`);
|
|
@@ -353,6 +357,20 @@ function displaySubQueryResult(info) {
|
|
|
353
357
|
}
|
|
354
358
|
console.log(` ${c.magenta}└─${c.reset} ${c.dim}${elapsed}s · ${formatSize(info.resultLength)} received${c.reset}`);
|
|
355
359
|
}
|
|
360
|
+
// ── Available models list ────────────────────────────────────────────────────
|
|
361
|
+
/** Collect models from providers that have an API key set. */
|
|
362
|
+
function getAvailableModels() {
|
|
363
|
+
const items = [];
|
|
364
|
+
for (const provider of getProviders()) {
|
|
365
|
+
const providerKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
366
|
+
if (!process.env[providerKey] && provider !== detectProvider())
|
|
367
|
+
continue;
|
|
368
|
+
for (const m of getModels(provider)) {
|
|
369
|
+
items.push({ id: m.id, provider });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return items;
|
|
373
|
+
}
|
|
356
374
|
// ── Truncate helper ─────────────────────────────────────────────────────────
|
|
357
375
|
function truncateStr(text, max) {
|
|
358
376
|
return text.length <= max ? text : text.slice(0, max - 3) + "...";
|
|
@@ -595,9 +613,13 @@ async function interactive() {
|
|
|
595
613
|
// Validate env
|
|
596
614
|
const hasApiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
597
615
|
if (!hasApiKey) {
|
|
598
|
-
|
|
599
|
-
console.log(`
|
|
600
|
-
|
|
616
|
+
printBanner();
|
|
617
|
+
console.log(` ${c.red}No API key found.${c.reset}\n`);
|
|
618
|
+
console.log(` Set one of these environment variables:\n`);
|
|
619
|
+
console.log(` ${c.yellow}export ANTHROPIC_API_KEY=sk-ant-...${c.reset} ${c.dim}# Anthropic (Claude)${c.reset}`);
|
|
620
|
+
console.log(` ${c.yellow}export OPENAI_API_KEY=sk-...${c.reset} ${c.dim}# OpenAI (GPT)${c.reset}\n`);
|
|
621
|
+
console.log(` ${c.dim}Add to your shell profile (~/.zshrc or ~/.bashrc) to persist across sessions.${c.reset}\n`);
|
|
622
|
+
process.exit(0);
|
|
601
623
|
}
|
|
602
624
|
// Resolve model
|
|
603
625
|
currentModel = resolveModel(currentModelId);
|
|
@@ -613,6 +635,21 @@ async function interactive() {
|
|
|
613
635
|
prompt: `${c.cyan}>${c.reset} `,
|
|
614
636
|
terminal: true,
|
|
615
637
|
});
|
|
638
|
+
// Color slash commands cyan as the user types
|
|
639
|
+
const rlAny = rl;
|
|
640
|
+
const promptStr = rl.getPrompt();
|
|
641
|
+
rlAny._writeToOutput = function (str) {
|
|
642
|
+
if (!rlAny.line?.startsWith("/")) {
|
|
643
|
+
rlAny.output.write(str);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (str.startsWith(promptStr)) {
|
|
647
|
+
rlAny.output.write(promptStr + c.cyan + str.slice(promptStr.length) + c.reset);
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
rlAny.output.write(c.cyan + str + c.reset);
|
|
651
|
+
}
|
|
652
|
+
};
|
|
616
653
|
rl.prompt();
|
|
617
654
|
rl.on("line", async (rawLine) => {
|
|
618
655
|
if (isRunning)
|
|
@@ -675,6 +712,40 @@ async function interactive() {
|
|
|
675
712
|
contextSource = "";
|
|
676
713
|
console.log(` ${c.green}✓${c.reset} Context cleared.`);
|
|
677
714
|
break;
|
|
715
|
+
case "model":
|
|
716
|
+
case "m": {
|
|
717
|
+
const available = getAvailableModels();
|
|
718
|
+
if (arg) {
|
|
719
|
+
// Accept a number or a model ID
|
|
720
|
+
const pick = /^\d+$/.test(arg) ? available[parseInt(arg, 10) - 1]?.id : arg;
|
|
721
|
+
const newModel = pick ? resolveModel(pick) : undefined;
|
|
722
|
+
if (newModel && pick) {
|
|
723
|
+
currentModelId = pick;
|
|
724
|
+
currentModel = newModel;
|
|
725
|
+
console.log(` ${c.green}✓${c.reset} Switched to ${c.bold}${currentModelId}${c.reset}`);
|
|
726
|
+
console.log();
|
|
727
|
+
printStatusLine();
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
console.log(` ${c.red}Model "${arg}" not found.${c.reset} Use ${c.cyan}/model${c.reset} to list available models.`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
console.log(`\n ${c.bold}Current model:${c.reset} ${c.cyan}${currentModelId}${c.reset}\n`);
|
|
735
|
+
const pad = String(available.length).length;
|
|
736
|
+
for (let i = 0; i < available.length; i++) {
|
|
737
|
+
const m = available[i];
|
|
738
|
+
const num = String(i + 1).padStart(pad);
|
|
739
|
+
const dot = m.id === currentModelId ? `${c.green}●${c.reset}` : ` `;
|
|
740
|
+
const label = m.id === currentModelId
|
|
741
|
+
? `${c.cyan}${m.id}${c.reset}`
|
|
742
|
+
: `${c.dim}${m.id}${c.reset}`;
|
|
743
|
+
console.log(` ${c.dim}${num}${c.reset} ${dot} ${label}`);
|
|
744
|
+
}
|
|
745
|
+
console.log(`\n ${c.dim}Type${c.reset} ${c.cyan}/model <number>${c.reset} ${c.dim}or${c.reset} ${c.cyan}/model <id>${c.reset} ${c.dim}to switch.${c.reset}`);
|
|
746
|
+
}
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
678
749
|
case "trajectories":
|
|
679
750
|
case "traj":
|
|
680
751
|
handleTrajectories();
|
|
@@ -689,7 +760,7 @@ async function interactive() {
|
|
|
689
760
|
process.exit(0);
|
|
690
761
|
break;
|
|
691
762
|
default:
|
|
692
|
-
console.log(` ${c.red}Unknown command: /${cmd}${c.reset}. Type ${c.
|
|
763
|
+
console.log(` ${c.red}Unknown command: /${cmd}${c.reset}. Type ${c.cyan}/help${c.reset} for commands.`);
|
|
693
764
|
}
|
|
694
765
|
rl.prompt();
|
|
695
766
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rlm-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Standalone CLI for Recursive Language Models (RLMs) — implements Algorithm 1 from arXiv:2512.24601",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
],
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@mariozechner/pi-ai": "^0.
|
|
35
|
+
"@mariozechner/pi-ai": "^0.55.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"esbuild": "^0.27.3",
|