@leroylabs/cli 0.1.8 → 0.1.9

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Leroy Labs CLI Proprietary License
2
+
3
+ Copyright © 2026 Leroy Labs. All rights reserved.
4
+
5
+ Leroy Labs grants you a limited, revocable, non-exclusive,
6
+ non-transferable, non-sublicensable license to install and use an unmodified
7
+ copy of this package solely to access the Leroy service in accordance with the
8
+ Leroy Terms at https://getleroy.com/legal.
9
+
10
+ Except for that limited license, Leroy Labs retains all right, title, and
11
+ interest in and to this package, including its source code, interfaces,
12
+ documentation, trademarks, and related intellectual property. You may not
13
+ copy, modify, create derivative works from, distribute, publish, sublicense,
14
+ sell, lease, reverse engineer, decompile, disassemble, or otherwise make this
15
+ package or its source code available to any third party, except to the extent
16
+ such restriction is prohibited by applicable law.
17
+
18
+ This package may include third-party components governed by their respective
19
+ license terms. Nothing in this license grants rights to Leroy Labs trademarks
20
+ or branding.
package/README.md CHANGED
@@ -55,23 +55,31 @@ which matches the one-minute tape cadence; use `--interval SECONDS` to choose a
55
55
  different polling interval. Each refresh is an authenticated evaluation request
56
56
  and may count toward the account's lookup allowance.
57
57
 
58
- ## Match strength
58
+ ## Evidence Strength
59
59
 
60
60
  Leroy selects the strongest available evidence tier and does not pool lower
61
61
  tiers into it:
62
62
 
63
- - `5/5`: exact ticker setup
64
- - `4/5`: same setup on other tickers
65
- - `3/5`: same 41-sensor state
66
- - `2/5`: same side with strategy and market context
67
- - `1/5`: same side baseline
63
+ - `5/5`: Exact Setup
64
+ - `4/5`: Cross-Ticker Setup
65
+ - `3/5`: Signal Pattern
66
+ - `2/5`: Contextual Analogue
67
+ - `1/5`: Direction-Only Baseline
68
+ - `0/5`: No Comparable Evidence
68
69
 
69
- The human-readable output shows `Exact Matches` only for `5/5`. Lower tiers
70
- show only their own `Comparable Matches` count. Use `--verbose` for the match
71
- basis, or `--json` for the complete structured response.
70
+ The human-readable output shows a scope-specific observation count according to
71
+ the selected tier; these are observations in that evidence scope, not setup
72
+ matches. Use `--verbose` for the full match basis, or `--json` for the complete
73
+ structured response.
72
74
 
73
75
  The browser reference is available at [getleroy.com/cli](https://getleroy.com/cli).
74
76
 
77
+ ## License
78
+
79
+ Copyright © 2026 Leroy Labs. This package is proprietary software, licensed
80
+ only for use with Leroy under the [Leroy Terms](https://getleroy.com/legal).
81
+ See [LICENSE](./LICENSE) for the package license.
82
+
75
83
  ## Exit codes
76
84
 
77
85
  - `0`: the request completed and returned a valid Leroy response. This includes
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@leroylabs/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Command-line access to Leroy market evidence.",
5
5
  "homepage": "https://getleroy.com",
6
+ "license": "SEE LICENSE IN LICENSE",
6
7
  "type": "module",
7
8
  "files": [
8
9
  "bin",
9
10
  "src",
10
- "README.md"
11
+ "README.md",
12
+ "LICENSE"
11
13
  ],
12
14
  "publishConfig": {
13
15
  "access": "public"
package/src/format.mjs CHANGED
@@ -131,7 +131,8 @@ function memoryMatchCounts(response) {
131
131
  const rowCount = finite(record(selected).row_count) ?? 0;
132
132
  return {
133
133
  exact: tier.score === 5 ? rowCount : 0,
134
- comparable: tier.score > 0 && tier.score < 5 ? rowCount : 0,
134
+ comparable: tier.score > 1 && tier.score < 5 ? rowCount : 0,
135
+ baseline: tier.score === 1 ? rowCount : 0,
135
136
  tier,
136
137
  };
137
138
  }
@@ -141,21 +142,67 @@ function matchTier(match) {
141
142
  const kind = value.match_kind;
142
143
  const scope = typeof value.scope === "string" ? value.scope : null;
143
144
  if (scope === "same_ticker_exact" || (kind === "context" && !scope)) {
144
- return { score: 5, basis: "Exact ticker setup" };
145
+ return { score: 5, basis: "Exact Setup" };
145
146
  }
146
147
  if (scope === "cross_ticker_exact_state" || kind === "cross_ticker_context") {
147
- return { score: 4, basis: "Same setup on other tickers" };
148
+ return { score: 4, basis: "Cross-Ticker Setup" };
148
149
  }
149
150
  if (scope === "exact_41_sensor_state" || kind === "strategy") {
150
- return { score: 3, basis: "Same 41-sensor state" };
151
+ return { score: 3, basis: "Signal Pattern" };
151
152
  }
152
153
  if (scope?.startsWith("forward_overlay_") || kind === "forward_overlay_context") {
153
- return { score: 2, basis: "Same side with strategy and market context" };
154
+ return { score: 2, basis: "Contextual Analogue" };
154
155
  }
155
156
  if (scope === "all_market_memory_observations" || kind === "side") {
156
- return { score: 1, basis: "Same side baseline" };
157
+ return { score: 1, basis: "Direction-Only Baseline" };
157
158
  }
158
- return { score: 0, basis: null };
159
+ return { score: 0, basis: "No Comparable Evidence" };
160
+ }
161
+
162
+ function matchShortLabel(score) {
163
+ if (score >= 5) return "Exact Setup";
164
+ if (score === 4) return "Cross-Ticker Setup";
165
+ if (score === 3) return "Signal Pattern";
166
+ if (score === 2) return "Contextual Analogue";
167
+ if (score === 1) return "Direction-Only Baseline";
168
+ return "No Comparable Evidence";
169
+ }
170
+
171
+ function matchTone(score) {
172
+ if (score >= 4) return "green";
173
+ if (score >= 2) return "yellow";
174
+ if (score === 1) return "red";
175
+ return "inactive";
176
+ }
177
+
178
+ function matchCells(score, colorEnabled) {
179
+ const active = Math.max(0, Math.min(5, Number(score) || 0));
180
+ const activeTone = matchTone(active);
181
+ return Array.from({ length: 5 }, (_, index) => color(
182
+ index < active ? "●" : "○",
183
+ index < active ? activeTone : "inactive",
184
+ colorEnabled,
185
+ )).join("");
186
+ }
187
+
188
+ function matchEvidenceLabel(tier, rowCount) {
189
+ const labels = {
190
+ 5: "exact observations",
191
+ 4: "cross-ticker observations",
192
+ 3: "signal-pattern observations",
193
+ 2: "context observations",
194
+ 1: "baseline observations",
195
+ };
196
+ return labels[tier.score] ? `${rowCount} ${labels[tier.score]}` : "No comparable observations";
197
+ }
198
+
199
+ function matchCountMetric(tier, counts) {
200
+ if (tier.score === 5) return { label: "Exact Observations", value: countLabel(counts.exact) };
201
+ if (tier.score === 4) return { label: "Cross-Ticker Sample", value: countLabel(counts.comparable) };
202
+ if (tier.score === 3) return { label: "Signal Pattern Sample", value: countLabel(counts.comparable) };
203
+ if (tier.score === 2) return { label: "Context Sample", value: countLabel(counts.comparable) };
204
+ if (tier.score === 1) return { label: "Baseline Observations", value: countLabel(counts.baseline) };
205
+ return null;
159
206
  }
160
207
 
161
208
  function memoryStatus(response) {
@@ -193,7 +240,7 @@ function lookupMilliseconds(response) {
193
240
  function humanizeStatus(value) {
194
241
  const labels = {
195
242
  historical_match: "Historical Match",
196
- insufficient_context: "Limited Match",
243
+ insufficient_context: "No Comparable Evidence",
197
244
  artifact_unavailable: "Reader Unavailable",
198
245
  };
199
246
  if (typeof value !== "string" || !value) return "Unavailable";
@@ -202,7 +249,7 @@ function humanizeStatus(value) {
202
249
 
203
250
  function memorySummary(status, tier) {
204
251
  if (status === "historical_match" && tier.score === 5) return "Match Found";
205
- if (tier.score > 0) return "Limited Match";
252
+ if (tier.score > 0) return tier.basis;
206
253
  return humanizeStatus(status);
207
254
  }
208
255
 
@@ -333,7 +380,7 @@ function ratioLabel(value) {
333
380
  }
334
381
 
335
382
  function horizonLabel(horizon) {
336
- return `${horizon} Minutes`;
383
+ return `${horizon} Min`;
337
384
  }
338
385
 
339
386
  function setupLine(label, value, tone, enabled) {
@@ -453,17 +500,10 @@ function gridFrameParts(value) {
453
500
 
454
501
  function gridMatchColumns(match, colorEnabled) {
455
502
  const tier = matchTier(match);
456
- const label = tier.score === 5 ? "Exact" : tier.score > 0 ? "Limited" : "No match";
457
503
  const rowCount = countLabel(record(match).row_count);
458
- const countLabelText = tier.score === 5 ? "exact observations" : "comparable observations";
459
- const cells = Array.from({ length: 5 }, (_, index) => color(
460
- index < tier.score ? "■" : "□",
461
- index < tier.score ? "green" : "neutral",
462
- colorEnabled,
463
- )).join("");
464
504
  return {
465
- label: `${cells} ${label}`,
466
- comparable: `${rowCount} ${countLabelText}`,
505
+ label: `${matchCells(tier.score, colorEnabled)} ${matchShortLabel(tier.score)}`,
506
+ comparable: matchEvidenceLabel(tier, rowCount),
467
507
  };
468
508
  }
469
509
 
@@ -540,7 +580,7 @@ export function buildEvaluationViewModel(response) {
540
580
  statusReason: memory.reason,
541
581
  match: {
542
582
  score: tier.score,
543
- label: tier.score === 5 ? "Exact" : tier.score > 0 ? "Limited" : "No match",
583
+ label: matchShortLabel(tier.score),
544
584
  rowCount: countLabel(record(match).row_count),
545
585
  basis: tier.basis,
546
586
  },
@@ -581,7 +621,7 @@ function formatGridEvaluation(response, { colorEnabled = true } = {}) {
581
621
  const topLines = [
582
622
  gridColumns(
583
623
  { text: `${sideText} EVIDENCE`, tone: sideTone(requestedSide) },
584
- { text: "MATCH STRENGTH", tone: "white" },
624
+ { text: "EVIDENCE STRENGTH", tone: "white" },
585
625
  colorEnabled,
586
626
  ),
587
627
  gridColumns(
@@ -700,6 +740,7 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
700
740
  ...strategyMixLines(counts.counts, strategyTotal, colorEnabled),
701
741
  sectionBottom(colorEnabled),
702
742
  ];
743
+ const matchCount = matchCountMetric(matchCounts.tier, matchCounts);
703
744
  const outcomeGrid = [
704
745
  sectionRule("Outcome", colorEnabled),
705
746
  outcomeRow("Timeframe", outcomeRows.map((row) => ({ value: horizonLabel(row.horizon), tone: "white" })), colorEnabled),
@@ -733,10 +774,9 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
733
774
  "",
734
775
  "Market Memory",
735
776
  compactMetricLine("Status", memorySummary(status, matchCounts.tier), statusTone, colorEnabled),
736
- compactMetricLine("Match Strength", `${matchCounts.tier.score}/5`, matchCounts.tier.score === 5 ? "green" : matchCounts.tier.score > 0 ? "yellow" : "dim", colorEnabled),
737
- ...(matchCounts.tier.score === 5 ? [compactMetricLine("Exact Matches", countLabel(matchCounts.exact), "bold", colorEnabled)] : []),
738
- ...(matchCounts.tier.score > 0 && matchCounts.tier.score < 5 ? [compactMetricLine("Comparable Matches", countLabel(matchCounts.comparable), "bold", colorEnabled)] : []),
739
- ...(verbose && matchCounts.tier.basis ? [compactMetricLine("Match Basis", matchCounts.tier.basis, "dim", colorEnabled)] : []),
777
+ compactMetricLine("Evidence Strength", `${matchCounts.tier.score}/5`, matchCounts.tier.score === 5 ? "green" : matchCounts.tier.score > 0 ? "yellow" : "dim", colorEnabled),
778
+ ...(matchCount ? [compactMetricLine(matchCount.label, matchCount.value, "bold", colorEnabled)] : []),
779
+ ...(verbose && matchCounts.tier.basis ? [compactMetricLine("Evidence Basis", matchCounts.tier.basis, "dim", colorEnabled)] : []),
740
780
  compactMetricLine("Window", coverageWindow(response, match), "dim", colorEnabled),
741
781
  "",
742
782
  "Outcome",
@@ -755,10 +795,9 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
755
795
  "",
756
796
  sectionRule("Market Memory", colorEnabled),
757
797
  metricLine("Status", memorySummary(status, matchCounts.tier), statusTone, colorEnabled, 24),
758
- metricLine("Match Strength", `${matchCounts.tier.score}/5`, matchCounts.tier.score === 5 ? "green" : matchCounts.tier.score > 0 ? "yellow" : "dim", colorEnabled, 24),
759
- ...(matchCounts.tier.score === 5 ? [metricLine("Exact Matches", countLabel(matchCounts.exact), "bold", colorEnabled, 24)] : []),
760
- ...(matchCounts.tier.score > 0 && matchCounts.tier.score < 5 ? [metricLine("Comparable Matches", countLabel(matchCounts.comparable), "bold", colorEnabled, 24)] : []),
761
- ...(verbose && matchCounts.tier.basis ? [metricLine("Match Basis", matchCounts.tier.basis, "dim", colorEnabled, 24)] : []),
798
+ metricLine("Evidence Strength", `${matchCounts.tier.score}/5`, matchCounts.tier.score === 5 ? "green" : matchCounts.tier.score > 0 ? "yellow" : "dim", colorEnabled, 24),
799
+ ...(matchCount ? [metricLine(matchCount.label, matchCount.value, "bold", colorEnabled, 24)] : []),
800
+ ...(verbose && matchCounts.tier.basis ? [metricLine("Evidence Basis", matchCounts.tier.basis, "dim", colorEnabled, 24)] : []),
762
801
  metricLine("Window", coverageWindow(response, match), "dim", colorEnabled, 24),
763
802
  sectionBottom(colorEnabled),
764
803
  "",
package/src/live-ui.mjs CHANGED
@@ -235,7 +235,7 @@ function renderOutcomeTable(outcomes, colorEnabled) {
235
235
  const rows = [
236
236
  [
237
237
  tableValue("Timeframe", "muted", colorEnabled, { bold: true }),
238
- ...outcomes.map((outcome) => tableValue(`${outcome.horizon} Minutes`, "white", colorEnabled, { bold: true, hAlign: "right" })),
238
+ ...outcomes.map((outcome) => tableValue(`${outcome.horizon} Min`, "white", colorEnabled, { bold: true, hAlign: "right" })),
239
239
  ],
240
240
  [
241
241
  tableValue("Avg. Return", "muted", colorEnabled, { bold: true }),
@@ -312,24 +312,30 @@ function bodyLine(content, colorEnabled, layout) {
312
312
  }
313
313
 
314
314
  function renderHistoricalEvidence(view, colorEnabled) {
315
- const sampleLabel = view.match.score === 5
316
- ? " exact observations"
317
- : view.match.score > 0
318
- ? " comparable observations"
319
- : " observations";
320
- const matchLabel = view.match.label === "No match" ? "No Match" : `${view.match.label} Match`;
321
- const matchDetails = view.match.score > 0 ? `${view.match.rowCount}${sampleLabel}` : "No comparable observations.";
315
+ const score = view.match.score;
316
+ const activeTone = score >= 4 ? "green" : score >= 2 ? "yellow" : score === 1 ? "red" : "inactive";
317
+ const detailLabels = {
318
+ 5: "exact observations",
319
+ 4: "cross-ticker observations",
320
+ 3: "signal-pattern observations",
321
+ 2: "context observations",
322
+ 1: "baseline observations",
323
+ };
324
+ const matchDetails = detailLabels[score]
325
+ ? `${view.match.rowCount} ${detailLabels[score]}`
326
+ : "No comparable observations";
322
327
  const matchStrength = [
323
328
  Array.from({ length: 5 }, (_, index) => tone(
324
- strengthCellGlyph(index < view.match.score, colorEnabled),
325
- index < view.match.score ? "green" : "neutral",
329
+ index < score ? "●" : "○",
330
+ index < score ? activeTone : "inactive",
326
331
  colorEnabled,
327
- )).join(" "),
328
- tone(matchLabel, view.match.score > 0 ? "white" : "muted", colorEnabled, true),
332
+ )).join(""),
333
+ tone(`${score}/5`, score > 0 ? "white" : "muted", colorEnabled, true),
329
334
  ].join(" ");
330
335
  const rows = [
331
- [tableValue("Match Strength", "muted", colorEnabled, { bold: true }), { content: matchStrength, hAlign: "left" }],
332
- [tableValue("Match Details", "muted", colorEnabled, { bold: true }), tableValue(matchDetails, "neutral", colorEnabled, { bold: true })],
336
+ [tableValue("Evidence Strength", "muted", colorEnabled, { bold: true }), { content: matchStrength, hAlign: "left" }],
337
+ [tableValue("Evidence Basis", "muted", colorEnabled, { bold: true }), tableValue(view.match.basis ?? "No Comparable Evidence", "neutral", colorEnabled)],
338
+ [tableValue("Observations", "muted", colorEnabled, { bold: true }), tableValue(matchDetails, "neutral", colorEnabled, { bold: true })],
333
339
  [tableValue("Date Range", "muted", colorEnabled, { bold: true }), tableValue(view.archiveWindow, "neutral", colorEnabled)],
334
340
  ];
335
341
  return renderTable(rows, tableColumnWidths(rows), colorEnabled, { divider: false });