@leroylabs/cli 0.1.7 → 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 +20 -0
- package/README.md +17 -9
- package/package.json +4 -2
- package/src/format.mjs +96 -29
- package/src/live-ui.mjs +39 -15
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
|
-
##
|
|
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`:
|
|
64
|
-
- `4/5`:
|
|
65
|
-
- `3/5`:
|
|
66
|
-
- `2/5`:
|
|
67
|
-
- `1/5`:
|
|
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
|
|
70
|
-
|
|
71
|
-
basis, or `--json` for the complete
|
|
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.
|
|
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
|
@@ -25,6 +25,32 @@ function record(value) {
|
|
|
25
25
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export function unavailableSymbolGuidance(response) {
|
|
29
|
+
if (response?.status !== "unavailable") return null;
|
|
30
|
+
const assumptions = record(response.assumptions);
|
|
31
|
+
if (assumptions.availability_code !== "symbol_not_covered") return null;
|
|
32
|
+
const request = record(response.request);
|
|
33
|
+
const symbol = typeof request.symbol === "string" ? request.symbol : null;
|
|
34
|
+
if (!symbol) return null;
|
|
35
|
+
const suggestedSymbol = typeof assumptions.suggested_symbol === "string"
|
|
36
|
+
? assumptions.suggested_symbol.toUpperCase()
|
|
37
|
+
: null;
|
|
38
|
+
return { symbol, suggestedSymbol };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatUnavailableSymbolGuidance(response, colorEnabled) {
|
|
42
|
+
const guidance = unavailableSymbolGuidance(response);
|
|
43
|
+
if (!guidance) return null;
|
|
44
|
+
return [
|
|
45
|
+
`${style("✕", ["bold", "red"], colorEnabled)} ${style(`${guidance.symbol} is not currently covered by Leroy.`, ["white"], colorEnabled)}`,
|
|
46
|
+
...(guidance.suggestedSymbol ? [
|
|
47
|
+
"",
|
|
48
|
+
` ${style(`Did you mean ${guidance.suggestedSymbol}?`, ["white"], colorEnabled)}`,
|
|
49
|
+
` ${color(`Retry: leroy evaluate ${guidance.suggestedSymbol}`, "neutral", colorEnabled)}`,
|
|
50
|
+
] : []),
|
|
51
|
+
].join("\n");
|
|
52
|
+
}
|
|
53
|
+
|
|
28
54
|
function finite(value) {
|
|
29
55
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
30
56
|
}
|
|
@@ -105,7 +131,8 @@ function memoryMatchCounts(response) {
|
|
|
105
131
|
const rowCount = finite(record(selected).row_count) ?? 0;
|
|
106
132
|
return {
|
|
107
133
|
exact: tier.score === 5 ? rowCount : 0,
|
|
108
|
-
comparable: tier.score >
|
|
134
|
+
comparable: tier.score > 1 && tier.score < 5 ? rowCount : 0,
|
|
135
|
+
baseline: tier.score === 1 ? rowCount : 0,
|
|
109
136
|
tier,
|
|
110
137
|
};
|
|
111
138
|
}
|
|
@@ -115,21 +142,67 @@ function matchTier(match) {
|
|
|
115
142
|
const kind = value.match_kind;
|
|
116
143
|
const scope = typeof value.scope === "string" ? value.scope : null;
|
|
117
144
|
if (scope === "same_ticker_exact" || (kind === "context" && !scope)) {
|
|
118
|
-
return { score: 5, basis: "Exact
|
|
145
|
+
return { score: 5, basis: "Exact Setup" };
|
|
119
146
|
}
|
|
120
147
|
if (scope === "cross_ticker_exact_state" || kind === "cross_ticker_context") {
|
|
121
|
-
return { score: 4, basis: "
|
|
148
|
+
return { score: 4, basis: "Cross-Ticker Setup" };
|
|
122
149
|
}
|
|
123
150
|
if (scope === "exact_41_sensor_state" || kind === "strategy") {
|
|
124
|
-
return { score: 3, basis: "
|
|
151
|
+
return { score: 3, basis: "Signal Pattern" };
|
|
125
152
|
}
|
|
126
153
|
if (scope?.startsWith("forward_overlay_") || kind === "forward_overlay_context") {
|
|
127
|
-
return { score: 2, basis: "
|
|
154
|
+
return { score: 2, basis: "Contextual Analogue" };
|
|
128
155
|
}
|
|
129
156
|
if (scope === "all_market_memory_observations" || kind === "side") {
|
|
130
|
-
return { score: 1, basis: "
|
|
157
|
+
return { score: 1, basis: "Direction-Only Baseline" };
|
|
131
158
|
}
|
|
132
|
-
return { score: 0, basis:
|
|
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;
|
|
133
206
|
}
|
|
134
207
|
|
|
135
208
|
function memoryStatus(response) {
|
|
@@ -167,7 +240,7 @@ function lookupMilliseconds(response) {
|
|
|
167
240
|
function humanizeStatus(value) {
|
|
168
241
|
const labels = {
|
|
169
242
|
historical_match: "Historical Match",
|
|
170
|
-
insufficient_context: "
|
|
243
|
+
insufficient_context: "No Comparable Evidence",
|
|
171
244
|
artifact_unavailable: "Reader Unavailable",
|
|
172
245
|
};
|
|
173
246
|
if (typeof value !== "string" || !value) return "Unavailable";
|
|
@@ -176,7 +249,7 @@ function humanizeStatus(value) {
|
|
|
176
249
|
|
|
177
250
|
function memorySummary(status, tier) {
|
|
178
251
|
if (status === "historical_match" && tier.score === 5) return "Match Found";
|
|
179
|
-
if (tier.score > 0) return
|
|
252
|
+
if (tier.score > 0) return tier.basis;
|
|
180
253
|
return humanizeStatus(status);
|
|
181
254
|
}
|
|
182
255
|
|
|
@@ -307,7 +380,7 @@ function ratioLabel(value) {
|
|
|
307
380
|
}
|
|
308
381
|
|
|
309
382
|
function horizonLabel(horizon) {
|
|
310
|
-
return `${horizon}
|
|
383
|
+
return `${horizon} Min`;
|
|
311
384
|
}
|
|
312
385
|
|
|
313
386
|
function setupLine(label, value, tone, enabled) {
|
|
@@ -427,17 +500,10 @@ function gridFrameParts(value) {
|
|
|
427
500
|
|
|
428
501
|
function gridMatchColumns(match, colorEnabled) {
|
|
429
502
|
const tier = matchTier(match);
|
|
430
|
-
const label = tier.score === 5 ? "Exact" : tier.score > 0 ? "Limited" : "No match";
|
|
431
503
|
const rowCount = countLabel(record(match).row_count);
|
|
432
|
-
const countLabelText = tier.score === 5 ? "exact observations" : "comparable observations";
|
|
433
|
-
const cells = Array.from({ length: 5 }, (_, index) => color(
|
|
434
|
-
index < tier.score ? "■" : "□",
|
|
435
|
-
index < tier.score ? "green" : "neutral",
|
|
436
|
-
colorEnabled,
|
|
437
|
-
)).join("");
|
|
438
504
|
return {
|
|
439
|
-
label: `${
|
|
440
|
-
comparable:
|
|
505
|
+
label: `${matchCells(tier.score, colorEnabled)} ${matchShortLabel(tier.score)}`,
|
|
506
|
+
comparable: matchEvidenceLabel(tier, rowCount),
|
|
441
507
|
};
|
|
442
508
|
}
|
|
443
509
|
|
|
@@ -514,7 +580,7 @@ export function buildEvaluationViewModel(response) {
|
|
|
514
580
|
statusReason: memory.reason,
|
|
515
581
|
match: {
|
|
516
582
|
score: tier.score,
|
|
517
|
-
label: tier.score
|
|
583
|
+
label: matchShortLabel(tier.score),
|
|
518
584
|
rowCount: countLabel(record(match).row_count),
|
|
519
585
|
basis: tier.basis,
|
|
520
586
|
},
|
|
@@ -555,7 +621,7 @@ function formatGridEvaluation(response, { colorEnabled = true } = {}) {
|
|
|
555
621
|
const topLines = [
|
|
556
622
|
gridColumns(
|
|
557
623
|
{ text: `${sideText} EVIDENCE`, tone: sideTone(requestedSide) },
|
|
558
|
-
{ text: "
|
|
624
|
+
{ text: "EVIDENCE STRENGTH", tone: "white" },
|
|
559
625
|
colorEnabled,
|
|
560
626
|
),
|
|
561
627
|
gridColumns(
|
|
@@ -586,6 +652,8 @@ function formatGridEvaluation(response, { colorEnabled = true } = {}) {
|
|
|
586
652
|
}
|
|
587
653
|
|
|
588
654
|
export function formatEvaluation(response, { colorEnabled = true, setup = false, verbose = false, compact = false, layout = "ledger" } = {}) {
|
|
655
|
+
const unavailableSymbol = formatUnavailableSymbolGuidance(response, colorEnabled);
|
|
656
|
+
if (unavailableSymbol) return unavailableSymbol;
|
|
589
657
|
if (layout === "grid") {
|
|
590
658
|
const { status } = memoryStatus(response);
|
|
591
659
|
return status === "artifact_unavailable"
|
|
@@ -672,6 +740,7 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
|
|
|
672
740
|
...strategyMixLines(counts.counts, strategyTotal, colorEnabled),
|
|
673
741
|
sectionBottom(colorEnabled),
|
|
674
742
|
];
|
|
743
|
+
const matchCount = matchCountMetric(matchCounts.tier, matchCounts);
|
|
675
744
|
const outcomeGrid = [
|
|
676
745
|
sectionRule("Outcome", colorEnabled),
|
|
677
746
|
outcomeRow("Timeframe", outcomeRows.map((row) => ({ value: horizonLabel(row.horizon), tone: "white" })), colorEnabled),
|
|
@@ -705,10 +774,9 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
|
|
|
705
774
|
"",
|
|
706
775
|
"Market Memory",
|
|
707
776
|
compactMetricLine("Status", memorySummary(status, matchCounts.tier), statusTone, colorEnabled),
|
|
708
|
-
compactMetricLine("
|
|
709
|
-
...(
|
|
710
|
-
...(
|
|
711
|
-
...(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)] : []),
|
|
712
780
|
compactMetricLine("Window", coverageWindow(response, match), "dim", colorEnabled),
|
|
713
781
|
"",
|
|
714
782
|
"Outcome",
|
|
@@ -727,10 +795,9 @@ export function formatEvaluation(response, { colorEnabled = true, setup = false,
|
|
|
727
795
|
"",
|
|
728
796
|
sectionRule("Market Memory", colorEnabled),
|
|
729
797
|
metricLine("Status", memorySummary(status, matchCounts.tier), statusTone, colorEnabled, 24),
|
|
730
|
-
metricLine("
|
|
731
|
-
...(
|
|
732
|
-
...(
|
|
733
|
-
...(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)] : []),
|
|
734
801
|
metricLine("Window", coverageWindow(response, match), "dim", colorEnabled, 24),
|
|
735
802
|
sectionBottom(colorEnabled),
|
|
736
803
|
"",
|
package/src/live-ui.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { stdin as processInput, stdout as processOutput } from "node:process";
|
|
2
2
|
|
|
3
3
|
import { ansiForeground, LEROY_COLORS } from "./colors.mjs";
|
|
4
|
-
import { buildEvaluationViewModel } from "./format.mjs";
|
|
4
|
+
import { buildEvaluationViewModel, unavailableSymbolGuidance } from "./format.mjs";
|
|
5
5
|
|
|
6
6
|
const MAX_CARD_WIDTH = 67;
|
|
7
7
|
const MIN_CARD_WIDTH = 64;
|
|
@@ -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}
|
|
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
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
325
|
-
index <
|
|
329
|
+
index < score ? "●" : "○",
|
|
330
|
+
index < score ? activeTone : "inactive",
|
|
326
331
|
colorEnabled,
|
|
327
|
-
)).join("
|
|
328
|
-
tone(
|
|
332
|
+
)).join(""),
|
|
333
|
+
tone(`${score}/5`, score > 0 ? "white" : "muted", colorEnabled, true),
|
|
329
334
|
].join(" ");
|
|
330
335
|
const rows = [
|
|
331
|
-
[tableValue("
|
|
332
|
-
[tableValue("
|
|
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 });
|
|
@@ -366,6 +372,24 @@ export function renderEvidenceView(response, { colorEnabled = true, columns = 80
|
|
|
366
372
|
? new Date(updatedAt).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })
|
|
367
373
|
: "—";
|
|
368
374
|
const liveTone = phase === "error" ? "red" : phase === "checking" ? "yellow" : "green";
|
|
375
|
+
const unavailableGuidance = unavailableSymbolGuidance(response);
|
|
376
|
+
if (unavailableGuidance) {
|
|
377
|
+
const lines = renderLoadingSteps({
|
|
378
|
+
symbol: unavailableGuidance.symbol,
|
|
379
|
+
progress,
|
|
380
|
+
colorEnabled,
|
|
381
|
+
columns,
|
|
382
|
+
}).split("\n");
|
|
383
|
+
lines.push("");
|
|
384
|
+
lines.push(`${layout.padding}${tone("✕", "red", colorEnabled, true)} ${tone(`${unavailableGuidance.symbol} is not currently covered by Leroy.`, "white", colorEnabled)}`);
|
|
385
|
+
if (unavailableGuidance.suggestedSymbol) {
|
|
386
|
+
lines.push("");
|
|
387
|
+
lines.push(`${layout.padding} ${tone(`Did you mean ${unavailableGuidance.suggestedSymbol}?`, "white", colorEnabled)}`);
|
|
388
|
+
lines.push(`${layout.padding} ${tone(`Retry: leroy evaluate ${unavailableGuidance.suggestedSymbol}`, "neutral", colorEnabled)}`);
|
|
389
|
+
}
|
|
390
|
+
lines.push("", "");
|
|
391
|
+
return lines.join("\n");
|
|
392
|
+
}
|
|
369
393
|
if (view.status === "artifact_unavailable") {
|
|
370
394
|
const lines = ["", ""];
|
|
371
395
|
lines.push(`${layout.padding}${tone("!", liveTone, colorEnabled, true)} ${tone("Market Memory unavailable", "white", colorEnabled)}`);
|