@leroylabs/cli 0.1.3 → 0.1.4
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 +9 -6
- package/bin/leroy.mjs +24 -5
- package/package.json +1 -1
- package/src/args.mjs +15 -7
- package/src/format.mjs +9 -8
- package/src/live-ui.mjs +0 -1
package/README.md
CHANGED
|
@@ -21,9 +21,9 @@ From the repository root:
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
npm run leroy -- help
|
|
24
|
-
npm run leroy --
|
|
25
|
-
npm run leroy -- watch
|
|
26
|
-
npm run leroy --
|
|
24
|
+
npm run leroy -- evaluate ABNB
|
|
25
|
+
npm run leroy -- watch ABNB
|
|
26
|
+
npm run leroy -- evaluate ABNB --demo
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
The command reads `LEROY_MCP_URL` and `LEROY_API_KEY` when present. Otherwise,
|
|
@@ -32,13 +32,16 @@ Leroy config.
|
|
|
32
32
|
|
|
33
33
|
The default endpoint is `https://getleroy.com/api/mcp`.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
`evaluate` and `watch` default to the buy case. Use `--side sell` or
|
|
36
|
+
`--side both` when you need to evaluate a different side.
|
|
37
|
+
|
|
38
|
+
Use `--demo` with `evaluate` or `watch` to render local randomized evidence without
|
|
36
39
|
an API key or network request. Each demo evaluation generates 41 individual
|
|
37
40
|
strategy cells plus varied market context, match strength, and outcomes so the
|
|
38
41
|
interactive UI can be explored outside market hours.
|
|
39
42
|
|
|
40
43
|
The CLI supports the authenticated `evaluate_current_setup` MCP tool. In an
|
|
41
|
-
interactive terminal, the default `leroy
|
|
44
|
+
interactive terminal, the default `leroy evaluate` view renders the evidence card
|
|
42
45
|
immediately and replaces its loading state with the authenticated result. Use
|
|
43
46
|
`--json` for agent and shell integrations, or `--setup`/`--verbose` when you
|
|
44
47
|
need the deterministic text formatter. A normal request reports current-frame
|
|
@@ -46,7 +49,7 @@ freshness, the registered and evaluated strategy counts, exact versus broader
|
|
|
46
49
|
Market Memory evidence, completed-label counts, and the research-only
|
|
47
50
|
boundary.
|
|
48
51
|
|
|
49
|
-
Use `leroy watch
|
|
52
|
+
Use `leroy watch ABNB` in an interactive terminal to keep the evidence view
|
|
50
53
|
open while the current tape advances. It refreshes once per minute by default,
|
|
51
54
|
which matches the one-minute tape cadence; use `--interval SECONDS` to choose a
|
|
52
55
|
different polling interval. Each refresh is an authenticated evaluation request
|
package/bin/leroy.mjs
CHANGED
|
@@ -11,6 +11,19 @@ import { createDemoResponse } from "../src/demo.mjs";
|
|
|
11
11
|
import { usageError } from "../src/errors.mjs";
|
|
12
12
|
import { formatEvaluation, formatHelp } from "../src/format.mjs";
|
|
13
13
|
import { LiveEvidenceApp, render } from "../src/live-ui.mjs";
|
|
14
|
+
import { ansiForeground, LEROY_COLORS } from "../src/colors.mjs";
|
|
15
|
+
|
|
16
|
+
const ANSI = {
|
|
17
|
+
reset: "\u001b[0m",
|
|
18
|
+
bold: "\u001b[1m",
|
|
19
|
+
white: "\u001b[37m",
|
|
20
|
+
green: ansiForeground(LEROY_COLORS.green),
|
|
21
|
+
neutral: ansiForeground(LEROY_COLORS.neutral),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function promptStyle(value, tones, enabled) {
|
|
25
|
+
return enabled ? `${tones.map((tone) => ANSI[tone]).join("")}${value}${ANSI.reset}` : value;
|
|
26
|
+
}
|
|
14
27
|
|
|
15
28
|
function print(value = "") {
|
|
16
29
|
output.write(`${value}\n`);
|
|
@@ -20,9 +33,15 @@ async function connect(options) {
|
|
|
20
33
|
const config = await resolvedConfig(options);
|
|
21
34
|
let apiKey = config.apiKey;
|
|
22
35
|
if (!apiKey && input.isTTY) {
|
|
23
|
-
|
|
36
|
+
const colorEnabled = output.isTTY && !options.noColor;
|
|
37
|
+
print();
|
|
38
|
+
print(`${promptStyle("▲", ["green"], colorEnabled)} ${promptStyle("Connect Leroy", ["bold", "white"], colorEnabled)}`);
|
|
39
|
+
print();
|
|
40
|
+
print(` ${promptStyle("Create or activate an MCP key at", ["neutral"], colorEnabled)}`);
|
|
41
|
+
print(` ${promptStyle("https://getleroy.com/mcp", ["green"], colorEnabled)}`);
|
|
42
|
+
print();
|
|
24
43
|
const readline = createInterface({ input, output });
|
|
25
|
-
apiKey = (await readline.question("Paste your lr_live_ key:
|
|
44
|
+
apiKey = (await readline.question(` ${promptStyle("›", ["green"], colorEnabled)} Paste your ${promptStyle("lr_live_", ["bold", "white"], colorEnabled)} key: `)).trim();
|
|
26
45
|
readline.close();
|
|
27
46
|
}
|
|
28
47
|
if (!apiKey) {
|
|
@@ -37,7 +56,7 @@ async function connect(options) {
|
|
|
37
56
|
return 0;
|
|
38
57
|
}
|
|
39
58
|
|
|
40
|
-
async function
|
|
59
|
+
async function evaluate(options) {
|
|
41
60
|
const request = {
|
|
42
61
|
symbol: options.symbol,
|
|
43
62
|
side: options.side,
|
|
@@ -80,7 +99,7 @@ async function ask(options) {
|
|
|
80
99
|
|
|
81
100
|
async function watch(options) {
|
|
82
101
|
if (!input.isTTY || !output.isTTY) {
|
|
83
|
-
throw usageError("`leroy watch` requires an interactive terminal. Use `leroy
|
|
102
|
+
throw usageError("`leroy watch` requires an interactive terminal. Use `leroy evaluate` for one-shot output.");
|
|
84
103
|
}
|
|
85
104
|
const request = {
|
|
86
105
|
symbol: options.symbol,
|
|
@@ -118,7 +137,7 @@ async function main() {
|
|
|
118
137
|
return 0;
|
|
119
138
|
}
|
|
120
139
|
if (options.command === "connect") return await connect(options);
|
|
121
|
-
if (options.command === "
|
|
140
|
+
if (options.command === "evaluate") return await evaluate(options);
|
|
122
141
|
if (options.command === "watch") return await watch(options);
|
|
123
142
|
throw usageError("Unknown command: " + options.command + ". Run `leroy help`.");
|
|
124
143
|
} catch (error) {
|
package/package.json
CHANGED
package/src/args.mjs
CHANGED
|
@@ -27,6 +27,7 @@ export function parseArgs(argv) {
|
|
|
27
27
|
intervalSeconds: 60,
|
|
28
28
|
};
|
|
29
29
|
const positional = [];
|
|
30
|
+
let sideFlagProvided = false;
|
|
30
31
|
for (let index = 0; index < rest.length; index += 1) {
|
|
31
32
|
const value = rest[index];
|
|
32
33
|
if (!value.startsWith("--")) {
|
|
@@ -59,7 +60,10 @@ export function parseArgs(argv) {
|
|
|
59
60
|
continue;
|
|
60
61
|
}
|
|
61
62
|
const next = inline ?? rest[++index];
|
|
62
|
-
if (flag === "--side")
|
|
63
|
+
if (flag === "--side") {
|
|
64
|
+
options.side = next;
|
|
65
|
+
sideFlagProvided = true;
|
|
66
|
+
}
|
|
63
67
|
else if (flag === "--horizon") options.horizons = [Number(next)];
|
|
64
68
|
else if (flag === "--endpoint") options.endpoint = next;
|
|
65
69
|
else if (flag === "--api-key") options.apiKey = next;
|
|
@@ -69,21 +73,25 @@ export function parseArgs(argv) {
|
|
|
69
73
|
else if (flag === "--interval") options.intervalSeconds = numberValue(next, flag);
|
|
70
74
|
else throw usageError(`Unknown option: ${flag}`);
|
|
71
75
|
}
|
|
72
|
-
if (["
|
|
73
|
-
|
|
76
|
+
if (["evaluate", "watch"].includes(command) && ["buy", "sell", "both"].includes(positional[0]?.toLowerCase())) {
|
|
77
|
+
const positionalSide = positional[0].toLowerCase();
|
|
78
|
+
if (sideFlagProvided && options.side !== positionalSide) {
|
|
79
|
+
throw usageError("Specify the side once, using either a positional value or --side.");
|
|
80
|
+
}
|
|
81
|
+
if (!sideFlagProvided) options.side = positionalSide;
|
|
74
82
|
positional.shift();
|
|
75
83
|
}
|
|
76
84
|
options.symbol = positional[0] ? positional[0].toUpperCase() : null;
|
|
77
|
-
if (["
|
|
85
|
+
if (["evaluate", "watch"].includes(options.command)) {
|
|
78
86
|
if (!options.symbol || !/^[A-Z][A-Z0-9.-]{0,11}$/.test(options.symbol)) {
|
|
79
|
-
throw usageError(`Usage: leroy ${options.command} buy|sell|both
|
|
87
|
+
throw usageError(`Usage: leroy ${options.command} SYMBOL [--side buy|sell|both]`);
|
|
80
88
|
}
|
|
81
89
|
if (!["buy", "sell", "both"].includes(options.side)) throw usageError("--side must be buy, sell, or both.");
|
|
82
90
|
if (!options.horizons.every((horizon) => DEFAULT_HORIZONS.includes(horizon))) throw usageError("--horizon must be 5, 15, 30, or 60.");
|
|
83
91
|
if (options.command === "watch" && options.json) throw usageError("`leroy watch` cannot be combined with --json.");
|
|
84
92
|
}
|
|
85
|
-
if (options.demo && !["
|
|
86
|
-
throw usageError("`--demo` can only be used with `leroy
|
|
93
|
+
if (options.demo && !["evaluate", "watch"].includes(options.command)) {
|
|
94
|
+
throw usageError("`--demo` can only be used with `leroy evaluate` or `leroy watch`.");
|
|
87
95
|
}
|
|
88
96
|
return options;
|
|
89
97
|
}
|
package/src/format.mjs
CHANGED
|
@@ -747,16 +747,17 @@ export function formatHelp() {
|
|
|
747
747
|
"",
|
|
748
748
|
"Usage:",
|
|
749
749
|
" leroy connect [--api-key lr_live_...]",
|
|
750
|
-
" leroy
|
|
751
|
-
" leroy watch buy|sell|both
|
|
750
|
+
" leroy evaluate SYMBOL [--side buy|sell|both] [--setup] [--verbose] [--demo] [--horizon 5|15|30|60]",
|
|
751
|
+
" leroy watch SYMBOL [--side buy|sell|both] [--interval SECONDS] [--demo] [--horizon 5|15|30|60]",
|
|
752
|
+
" Buy is the default side; use --side sell or --side both to override it.",
|
|
752
753
|
"",
|
|
753
754
|
"Examples:",
|
|
754
|
-
" leroy
|
|
755
|
-
" leroy
|
|
756
|
-
" leroy
|
|
757
|
-
" leroy
|
|
758
|
-
" leroy
|
|
759
|
-
" leroy watch
|
|
755
|
+
" leroy evaluate ABNB",
|
|
756
|
+
" leroy evaluate ABNB --setup --verbose",
|
|
757
|
+
" leroy evaluate AAPL --side sell --horizon 30",
|
|
758
|
+
" leroy evaluate ABNB --json",
|
|
759
|
+
" leroy evaluate ABNB --demo",
|
|
760
|
+
" leroy watch ABNB",
|
|
760
761
|
"",
|
|
761
762
|
"Read the full CLI reference: https://getleroy.com/cli",
|
|
762
763
|
].join("\n");
|
package/src/live-ui.mjs
CHANGED
|
@@ -389,7 +389,6 @@ function QueryHeader({ view, colors }) {
|
|
|
389
389
|
},
|
|
390
390
|
React.createElement(Box, { flexDirection: "row", width: "100%", paddingRight: 2, justifyContent: "space-between" },
|
|
391
391
|
React.createElement(Text, { color: colors.white },
|
|
392
|
-
"ASK ",
|
|
393
392
|
React.createElement(Text, { color: sideTone(view.side, colors), bold: true }, side),
|
|
394
393
|
" · ",
|
|
395
394
|
React.createElement(Text, { color: colors.white, bold: true }, view.symbol),
|