@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 CHANGED
@@ -21,9 +21,9 @@ From the repository root:
21
21
 
22
22
  ```bash
23
23
  npm run leroy -- help
24
- npm run leroy -- ask buy ABNB
25
- npm run leroy -- watch buy ABNB
26
- npm run leroy -- ask buy ABNB --demo
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
- Use `--demo` with `ask` or `watch` to render local randomized evidence without
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 ask` view renders the evidence card
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 buy ABNB` in an interactive terminal to keep the evidence view
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
- print("Create or activate an MCP key at https://getleroy.com/mcp.");
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: ")).trim();
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 ask(options) {
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 ask` for one-shot output.");
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 === "ask") return await ask(options);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leroylabs/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Command-line access to Leroy market evidence.",
5
5
  "type": "module",
6
6
  "files": [
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") options.side = next;
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 (["ask", "watch"].includes(command) && ["buy", "sell", "both"].includes(positional[0]?.toLowerCase())) {
73
- if (options.side === "buy") options.side = positional[0].toLowerCase();
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 (["ask", "watch"].includes(options.command)) {
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 SYMBOL`);
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 && !["ask", "watch"].includes(options.command)) {
86
- throw usageError("`--demo` can only be used with `leroy ask` or `leroy watch`.");
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 ask buy|sell|both SYMBOL [--setup] [--verbose] [--demo] [--horizon 5|15|30|60]",
751
- " leroy watch buy|sell|both SYMBOL [--interval SECONDS] [--demo] [--horizon 5|15|30|60]",
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 ask buy ABNB",
755
- " leroy ask buy ABNB --setup --verbose",
756
- " leroy ask sell AAPL --horizon 30",
757
- " leroy ask buy ABNB --json",
758
- " leroy ask buy ABNB --demo",
759
- " leroy watch buy ABNB",
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),