moshcode 0.26.0 → 0.28.0
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/.claude-plugin/marketplace.json +12 -0
- package/README.md +56 -15
- package/bin/moshcode.mjs +11 -3
- package/package.json +1 -1
- package/plugins/crypto/.claude-plugin/plugin.json +13 -0
- package/plugins/crypto/README.md +66 -0
- package/plugins/crypto/commands/bars.md +35 -0
- package/plugins/crypto/commands/book.md +31 -0
- package/plugins/crypto/commands/coin.md +31 -0
- package/plugins/crypto/commands/crypto.md +50 -0
- package/plugins/crypto/commands/pairs.md +31 -0
- package/plugins/crypto/commands/quote.md +31 -0
- package/plugins/crypto/commands/spark.md +36 -0
- package/plugins/ticker/README.md +8 -2
- package/plugins/ticker/commands/discover.md +2 -2
- package/plugins/ticker/commands/lookup.md +3 -3
- package/plugins/ticker/commands/reports.md +3 -3
- package/plugins/ticker/commands/research.md +2 -2
- package/plugins/ticker/commands/signals.md +2 -2
- package/plugins/ticker/commands/{ticker.md → stocks.md} +2 -2
- package/src/advisor.mjs +45 -35
- package/src/cli-schema.mjs +93 -24
- package/src/crypto.mjs +877 -0
- package/src/help.mjs +14 -0
- package/src/plugins.mjs +6 -1
- package/src/tui.mjs +18 -6
package/src/advisor.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `moshcode
|
|
1
|
+
// `moshcode stocks` — equity research from advis0r.com, in the pit.
|
|
2
2
|
//
|
|
3
3
|
// Same split as src/trade.mjs: argument translation is pure and testable, the
|
|
4
4
|
// network call is injectable, and rendering is a function of the decoded JSON.
|
|
@@ -18,7 +18,7 @@ export function advisorBase(env = process.env) {
|
|
|
18
18
|
return (raw || DEFAULT_ADVISOR_URL).replace(/\/+$/, "");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const USAGE = `usage: moshcode
|
|
21
|
+
const USAGE = `usage: moshcode stocks <symbol|verb> [args…]
|
|
22
22
|
|
|
23
23
|
<symbol> the stored research report for one ticker
|
|
24
24
|
report <symbol> same thing, when a symbol looks like a verb
|
|
@@ -40,17 +40,17 @@ const USAGE = `usage: moshcode ticker <symbol|verb> [args…]
|
|
|
40
40
|
Research aid, not advice. Every route is public, read-only, and served from
|
|
41
41
|
stored snapshots — see the generated-at stamp printed with each report.`;
|
|
42
42
|
|
|
43
|
-
export function
|
|
43
|
+
export function stocksUsage() {
|
|
44
44
|
return USAGE;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/** Verb names, in help order. cli-schema's
|
|
48
|
-
export const
|
|
47
|
+
/** Verb names, in help order. cli-schema's STOCKS_VERBS must match (drift test). */
|
|
48
|
+
export const STOCKS_VERB_NAMES = [
|
|
49
49
|
"report", "signals", "search", "lookup", "reports", "discover", "tickers", "stats", "open",
|
|
50
50
|
];
|
|
51
51
|
|
|
52
|
-
// Aliases exist because muscle memory differs: `/
|
|
53
|
-
// `/
|
|
52
|
+
// Aliases exist because muscle memory differs: `/stocks news AAPL` and
|
|
53
|
+
// `/stocks quotes AAPL` should not be errors when the intent is obvious.
|
|
54
54
|
const VERB_ALIASES = {
|
|
55
55
|
signal: "signals", news: "signals",
|
|
56
56
|
find: "search", grep: "search", q: "search",
|
|
@@ -66,7 +66,7 @@ const VERB_ALIASES = {
|
|
|
66
66
|
/** Resolve a first argument to a canonical verb, or null when it is a symbol. */
|
|
67
67
|
export function resolveVerb(word) {
|
|
68
68
|
const key = String(word ?? "").toLowerCase();
|
|
69
|
-
if (
|
|
69
|
+
if (STOCKS_VERB_NAMES.includes(key)) return key;
|
|
70
70
|
return VERB_ALIASES[key] ?? null;
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -114,13 +114,13 @@ function positiveInt(value, { max }) {
|
|
|
114
114
|
const SORTS = ["recent", "score", "ticker"];
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
* Translate `
|
|
117
|
+
* Translate `stocks` arguments into a request the caller can execute.
|
|
118
118
|
*
|
|
119
119
|
* Returns one of `{ usage }`, `{ error }`, or
|
|
120
120
|
* `{ verb, path, query, json, open? }` — never performs IO, so the whole
|
|
121
121
|
* argument surface is testable without a network.
|
|
122
122
|
*/
|
|
123
|
-
export function
|
|
123
|
+
export function stocksArgs(input = []) {
|
|
124
124
|
const args = input.map(String);
|
|
125
125
|
const jsonFlag = takeFlag(args, "--json", { boolean: true });
|
|
126
126
|
let rest = jsonFlag.rest;
|
|
@@ -131,26 +131,26 @@ export function tickerArgs(input = []) {
|
|
|
131
131
|
const horizonFlag = takeFlag(rest, "--horizon"); rest = horizonFlag.rest;
|
|
132
132
|
const providerFlag = takeFlag(rest, "--provider"); rest = providerFlag.rest;
|
|
133
133
|
|
|
134
|
-
if (limitFlag.missing) return { error: "
|
|
135
|
-
if (sortFlag.missing) return { error: `
|
|
136
|
-
if (horizonFlag.missing) return { error: "
|
|
137
|
-
if (providerFlag.missing) return { error: "
|
|
134
|
+
if (limitFlag.missing) return { error: "stocks --limit requires a positive number" };
|
|
135
|
+
if (sortFlag.missing) return { error: `stocks --sort requires one of ${SORTS.join(", ")}` };
|
|
136
|
+
if (horizonFlag.missing) return { error: "stocks --horizon requires 1 or 2" };
|
|
137
|
+
if (providerFlag.missing) return { error: "stocks --provider requires a name" };
|
|
138
138
|
|
|
139
139
|
// A limit above the server's own cap is silently clamped there; clamping here
|
|
140
140
|
// too keeps `--limit 9999` from reading like a promise the API never made.
|
|
141
141
|
const limit = limitFlag.value == null ? null : positiveInt(limitFlag.value, { max: 50 });
|
|
142
142
|
if (limitFlag.value != null && limit == null) {
|
|
143
|
-
return { error: "
|
|
143
|
+
return { error: "stocks --limit requires a positive number" };
|
|
144
144
|
}
|
|
145
145
|
if (sortFlag.value != null && !SORTS.includes(sortFlag.value.toLowerCase())) {
|
|
146
|
-
return { error: `
|
|
146
|
+
return { error: `stocks --sort must be one of ${SORTS.join(", ")}` };
|
|
147
147
|
}
|
|
148
148
|
if (horizonFlag.value != null && !["1", "2"].includes(String(horizonFlag.value))) {
|
|
149
|
-
return { error: "
|
|
149
|
+
return { error: "stocks --horizon must be 1 or 2" };
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
const stray = rest.find((arg) => arg.startsWith("-") && arg !== "-");
|
|
153
|
-
if (stray) return { error: `unknown
|
|
153
|
+
if (stray) return { error: `unknown stocks flag ${JSON.stringify(stray)}` };
|
|
154
154
|
|
|
155
155
|
const [first, ...tail] = rest;
|
|
156
156
|
if (!first) return { usage: true };
|
|
@@ -158,16 +158,16 @@ export function tickerArgs(input = []) {
|
|
|
158
158
|
const verb = resolveVerb(first);
|
|
159
159
|
const words = verb ? tail : rest;
|
|
160
160
|
|
|
161
|
-
// No verb → the first word is the ticker. `/
|
|
161
|
+
// No verb → the first word is the ticker. `/stocks AAPL` is the headline
|
|
162
162
|
// case and must stay the shortest thing anyone types.
|
|
163
163
|
const wantsReport = verb == null || verb === "report" || verb === "open";
|
|
164
164
|
if (wantsReport) {
|
|
165
165
|
const raw = words[0];
|
|
166
|
-
if (!raw) return { error: `
|
|
166
|
+
if (!raw) return { error: `stocks ${verb === "open" ? "open" : "report"} requires a ticker symbol` };
|
|
167
167
|
const symbol = normalizeSymbol(raw);
|
|
168
168
|
if (!symbol) {
|
|
169
169
|
return {
|
|
170
|
-
error: `${JSON.stringify(String(raw))} is not a ticker symbol — try: moshcode
|
|
170
|
+
error: `${JSON.stringify(String(raw))} is not a ticker symbol — try: moshcode stocks lookup ${String(raw)}`,
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
173
|
if (verb === "open") return { verb: "open", symbol, open: `/ticker/${encodeURIComponent(symbol)}`, json };
|
|
@@ -176,16 +176,16 @@ export function tickerArgs(input = []) {
|
|
|
176
176
|
|
|
177
177
|
if (verb === "signals") {
|
|
178
178
|
const symbol = normalizeSymbol(words[0]);
|
|
179
|
-
if (!words[0]) return { error: "
|
|
179
|
+
if (!words[0]) return { error: "stocks signals requires a ticker symbol" };
|
|
180
180
|
if (!symbol) {
|
|
181
|
-
return { error: `${JSON.stringify(String(words[0]))} is not a ticker symbol — try: moshcode
|
|
181
|
+
return { error: `${JSON.stringify(String(words[0]))} is not a ticker symbol — try: moshcode stocks lookup ${words[0]}` };
|
|
182
182
|
}
|
|
183
183
|
return { verb, symbol, path: "/api/signals", query: { ticker: symbol }, json };
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
if (verb === "search" || verb === "lookup") {
|
|
187
187
|
const q = words.join(" ").trim();
|
|
188
|
-
if (!q) return { error: `
|
|
188
|
+
if (!q) return { error: `stocks ${verb} requires something to look for` };
|
|
189
189
|
const path = verb === "search" ? "/api/search" : "/api/lookup";
|
|
190
190
|
return { verb, path, query: { q, ...(limit ? { limit: String(limit) } : {}) }, json };
|
|
191
191
|
}
|
|
@@ -221,7 +221,7 @@ export function tickerArgs(input = []) {
|
|
|
221
221
|
if (verb === "tickers") return { verb, path: "/api/tickers", query: {}, json };
|
|
222
222
|
if (verb === "stats") return { verb, path: "/api/stats", query: {}, json };
|
|
223
223
|
|
|
224
|
-
return { error: `unknown
|
|
224
|
+
return { error: `unknown stocks command ${JSON.stringify(String(first))}` };
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
/** Build the absolute URL for a translated request. */
|
|
@@ -248,7 +248,7 @@ export async function fetchAdvisor(request, { fetchImpl = globalThis.fetch, base
|
|
|
248
248
|
try {
|
|
249
249
|
const res = await fetchImpl(url, {
|
|
250
250
|
signal: controller.signal,
|
|
251
|
-
headers: { accept: "application/json", "user-agent": "moshcode-
|
|
251
|
+
headers: { accept: "application/json", "user-agent": "moshcode-stocks" },
|
|
252
252
|
});
|
|
253
253
|
const text = await res.text();
|
|
254
254
|
let data;
|
|
@@ -279,8 +279,18 @@ function compact(v) {
|
|
|
279
279
|
const n = Number(v);
|
|
280
280
|
if (!Number.isFinite(n)) return null;
|
|
281
281
|
const units = [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]];
|
|
282
|
-
for (
|
|
283
|
-
|
|
282
|
+
for (let i = 0; i < units.length; i++) {
|
|
283
|
+
const [size, suffix] = units[i];
|
|
284
|
+
if (Math.abs(n) < size) continue;
|
|
285
|
+
// Rounding to 2 decimals can push a value just under the next boundary up to
|
|
286
|
+
// a full thousand of this unit (999,999,999 → "1000M"); carry it to the next
|
|
287
|
+
// unit up (→ "1B") so the number never reads as an un-carried thousand.
|
|
288
|
+
const scaled = (n / size).toFixed(2);
|
|
289
|
+
if (Math.abs(Number(scaled)) >= 1000 && i > 0) {
|
|
290
|
+
const [upSize, upSuffix] = units[i - 1];
|
|
291
|
+
return `${(n / upSize).toFixed(2).replace(/\.?0+$/, "")}${upSuffix}`;
|
|
292
|
+
}
|
|
293
|
+
return `${scaled.replace(/\.?0+$/, "")}${suffix}`;
|
|
284
294
|
}
|
|
285
295
|
return String(n);
|
|
286
296
|
}
|
|
@@ -458,7 +468,7 @@ function renderLookup(d) {
|
|
|
458
468
|
const report = m.hasReport ? acid(" ✓ report") : ash(" · no report yet");
|
|
459
469
|
lines.push(` ${acid(String(m.symbol).padEnd(8))}${bone(clip(m.name, 44).padEnd(46))}${ash(String(m.exchange ?? ""))}${report}`);
|
|
460
470
|
}
|
|
461
|
-
lines.push("", ` ${ash("then:")} ${bone(`moshcode
|
|
471
|
+
lines.push("", ` ${ash("then:")} ${bone(`moshcode stocks ${matches[0].symbol}`)}`);
|
|
462
472
|
return lines.join("\n");
|
|
463
473
|
}
|
|
464
474
|
|
|
@@ -474,7 +484,7 @@ function renderReports(d) {
|
|
|
474
484
|
`${bone(money(r.lastPrice).padStart(11))} ${ash(clip(r.companyName, 28).padEnd(29))}${ash(day(r.generatedAt))}`,
|
|
475
485
|
);
|
|
476
486
|
}
|
|
477
|
-
lines.push("", ` ${ash("detail:")} ${bone(`moshcode
|
|
487
|
+
lines.push("", ` ${ash("detail:")} ${bone(`moshcode stocks ${reports[0].ticker}`)}`);
|
|
478
488
|
return lines.join("\n");
|
|
479
489
|
}
|
|
480
490
|
|
|
@@ -540,12 +550,12 @@ export function renderAdvisor(verb, data, { columns } = {}) {
|
|
|
540
550
|
}
|
|
541
551
|
|
|
542
552
|
/**
|
|
543
|
-
* Run a `
|
|
553
|
+
* Run a `stocks` invocation end to end. Returns a process exit code.
|
|
544
554
|
*
|
|
545
555
|
* `deps` exists so tests drive the whole command — parse, fetch, render — with
|
|
546
556
|
* no network and no stdout.
|
|
547
557
|
*/
|
|
548
|
-
export async function
|
|
558
|
+
export async function stocksCommand(argv = [], deps = {}) {
|
|
549
559
|
const {
|
|
550
560
|
out = (s) => console.log(s),
|
|
551
561
|
fail = (s) => console.error(s),
|
|
@@ -555,8 +565,8 @@ export async function tickerCommand(argv = [], deps = {}) {
|
|
|
555
565
|
columns = process.stdout.columns,
|
|
556
566
|
} = deps;
|
|
557
567
|
|
|
558
|
-
const request =
|
|
559
|
-
if (request.usage) { out(
|
|
568
|
+
const request = stocksArgs(argv);
|
|
569
|
+
if (request.usage) { out(stocksUsage()); return 0; }
|
|
560
570
|
if (request.error) { fail(danger(`✗ ${request.error}`)); return 1; }
|
|
561
571
|
|
|
562
572
|
if (request.verb === "open") {
|
|
@@ -577,7 +587,7 @@ export async function tickerCommand(argv = [], deps = {}) {
|
|
|
577
587
|
if (request.json) { out(JSON.stringify(res.data, null, 2)); return 1; }
|
|
578
588
|
fail(danger(`✗ ${message}`));
|
|
579
589
|
if (res.data?.didYouMean?.symbol) {
|
|
580
|
-
fail(` ${ash("try:")} ${bone(`moshcode
|
|
590
|
+
fail(` ${ash("try:")} ${bone(`moshcode stocks ${res.data.didYouMean.symbol}`)}`);
|
|
581
591
|
}
|
|
582
592
|
return 1;
|
|
583
593
|
}
|
package/src/cli-schema.mjs
CHANGED
|
@@ -290,14 +290,14 @@ export const CORE_CLI_COMMANDS = [
|
|
|
290
290
|
note: "buy/sell inject --dry-run unless --submit is present. Alpaca defaults to paper trading; live trading requires its separate --live opt-in.",
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
|
-
name: "
|
|
293
|
+
name: "stocks",
|
|
294
294
|
group: "tools",
|
|
295
295
|
description: "equity research from advis0r.com",
|
|
296
296
|
synopsis: [
|
|
297
|
-
["moshcode
|
|
298
|
-
["moshcode
|
|
297
|
+
["moshcode stocks <symbol>", "the stored research report for one ticker"],
|
|
298
|
+
["moshcode stocks <verb> [args…]", ""],
|
|
299
299
|
],
|
|
300
|
-
verbs: "
|
|
300
|
+
verbs: "STOCKS_VERBS",
|
|
301
301
|
flags: [
|
|
302
302
|
["--json", "print the raw API response", ""],
|
|
303
303
|
["--limit <n>", "cap results (search/lookup/reports/discover)", "the API's own default"],
|
|
@@ -306,16 +306,46 @@ export const CORE_CLI_COMMANDS = [
|
|
|
306
306
|
["--provider <p>", "discover: analysis provider", "offline"],
|
|
307
307
|
],
|
|
308
308
|
examples: [
|
|
309
|
-
["moshcode
|
|
310
|
-
["moshcode
|
|
311
|
-
["moshcode
|
|
312
|
-
["moshcode
|
|
313
|
-
["moshcode
|
|
309
|
+
["moshcode stocks NVDA", "score, technicals, thesis, signals, sources"],
|
|
310
|
+
["moshcode stocks lookup rivian", "company name → RIVN"],
|
|
311
|
+
["moshcode stocks signals AAPL", "what was actually said, with sources"],
|
|
312
|
+
["moshcode stocks search 'data center'", "across every indexed transcript"],
|
|
313
|
+
["moshcode stocks reports --limit 10", "the stored index, best score first"],
|
|
314
314
|
],
|
|
315
315
|
seeAlso: ["trade", "plugin", "tools"],
|
|
316
316
|
note: "research aid, not advice — reports are stored snapshots and every one prints when it was generated. Set MOSHCODE_ADVISOR_URL to point at another instance.",
|
|
317
317
|
},
|
|
318
|
-
{ name: "advisor", aliasOf: "
|
|
318
|
+
{ name: "advisor", aliasOf: "stocks", description: "alias for stocks" },
|
|
319
|
+
{
|
|
320
|
+
name: "crypto",
|
|
321
|
+
group: "tools",
|
|
322
|
+
description: "crypto market data from advis0r.com",
|
|
323
|
+
synopsis: [
|
|
324
|
+
["moshcode crypto <pair>", "the full report for one pair"],
|
|
325
|
+
["moshcode crypto <verb> [args…]", ""],
|
|
326
|
+
],
|
|
327
|
+
verbs: "CRYPTO_VERBS",
|
|
328
|
+
flags: [
|
|
329
|
+
["--json", "print the raw API response", ""],
|
|
330
|
+
["--timeframe <tf>", "bars: 1Min | 5Min | 15Min | 1Hour | 1Day | 1Week", "1Day"],
|
|
331
|
+
["--start <iso>", "bars: window start", "the API's own default"],
|
|
332
|
+
["--end <iso>", "bars: window end", "now"],
|
|
333
|
+
["--limit <n>", "cap results (bars/lookup)", "the API's own default"],
|
|
334
|
+
["--depth <n>", "book: levels per side", "10"],
|
|
335
|
+
["--period <p>", "spark: 24h | 7d", "24h"],
|
|
336
|
+
["--horizon <n>", "technicals: quarters the score looks ahead (1 or 2)", "2"],
|
|
337
|
+
],
|
|
338
|
+
examples: [
|
|
339
|
+
["moshcode crypto BTC", "price, technicals, score, supply, order book"],
|
|
340
|
+
["moshcode crypto lookup bitcoin", "asset name → BTC/USD"],
|
|
341
|
+
["moshcode crypto spark BTC ETH SOL", "24h closes as sparklines"],
|
|
342
|
+
["moshcode crypto bars ETH --timeframe 1Hour", "historical OHLCV"],
|
|
343
|
+
["moshcode crypto book BTC-USD --depth 5", "top of book, both sides"],
|
|
344
|
+
],
|
|
345
|
+
seeAlso: ["stocks", "trade", "plugin"],
|
|
346
|
+
note: "research aid, not advice — prices are Alpaca's US crypto venue alone and can differ materially from other exchanges. Crypto trades 24/7 with no circuit breakers. Set MOSHCODE_ADVISOR_URL to point at another instance.",
|
|
347
|
+
},
|
|
348
|
+
{ name: "coins", aliasOf: "crypto", description: "alias for crypto" },
|
|
319
349
|
{
|
|
320
350
|
name: "plugin",
|
|
321
351
|
group: "extend",
|
|
@@ -327,7 +357,7 @@ export const CORE_CLI_COMMANDS = [
|
|
|
327
357
|
["moshcode plugin install", "add the marketplace and install ticker"],
|
|
328
358
|
["moshcode plugin list", "what this marketplace ships, and what is installed"],
|
|
329
359
|
],
|
|
330
|
-
seeAlso: ["skill", "mcp", "
|
|
360
|
+
seeAlso: ["skill", "mcp", "stocks"],
|
|
331
361
|
note: "Claude Code is the only engine with a plugin primitive; the others are reported as skipped, exactly as they are for skills.",
|
|
332
362
|
},
|
|
333
363
|
{ name: "plugins", aliasOf: "plugin", description: "alias for plugin" },
|
|
@@ -509,36 +539,72 @@ export const DNS_VERBS = [
|
|
|
509
539
|
];
|
|
510
540
|
|
|
511
541
|
/**
|
|
512
|
-
* `
|
|
542
|
+
* `stocks`'s verbs.
|
|
513
543
|
*
|
|
514
544
|
* `report` exists so a symbol that collides with a verb name still has an
|
|
515
545
|
* unambiguous spelling; without it, the bare-symbol shortcut would have no
|
|
516
546
|
* escape hatch. src/advisor.mjs owns the parser and test/advisor.test.mjs
|
|
517
547
|
* fails when the two lists disagree.
|
|
518
548
|
*/
|
|
519
|
-
export const
|
|
520
|
-
{ name: "report", description: "the stored research report for one ticker", synopsis: [["moshcode
|
|
521
|
-
{ name: "signals", description: "every extracted signal for a ticker", synopsis: [["moshcode
|
|
549
|
+
export const STOCKS_VERBS = [
|
|
550
|
+
{ name: "report", description: "the stored research report for one ticker", synopsis: [["moshcode stocks report <symbol>", "same as `moshcode stocks <symbol>`"]] },
|
|
551
|
+
{ name: "signals", description: "every extracted signal for a ticker", synopsis: [["moshcode stocks signals <symbol>", ""]] },
|
|
522
552
|
{
|
|
523
553
|
name: "search", description: "full-text search across indexed transcripts",
|
|
524
|
-
synopsis: [["moshcode
|
|
554
|
+
synopsis: [["moshcode stocks search <words…> [--limit n]", ""]],
|
|
525
555
|
},
|
|
526
556
|
{
|
|
527
557
|
name: "lookup", description: "find a ticker by company name",
|
|
528
|
-
synopsis: [["moshcode
|
|
558
|
+
synopsis: [["moshcode stocks lookup <company…> [--limit n]", "rivian → RIVN"]],
|
|
529
559
|
},
|
|
530
560
|
{
|
|
531
561
|
name: "reports", description: "every stored report",
|
|
532
|
-
synopsis: [["moshcode
|
|
562
|
+
synopsis: [["moshcode stocks reports [--sort recent|score|ticker] [--limit n]", ""]],
|
|
533
563
|
},
|
|
534
564
|
{
|
|
535
565
|
name: "discover", description: "a ranked watchlist for a topic",
|
|
536
|
-
synopsis: [["moshcode
|
|
566
|
+
synopsis: [["moshcode stocks discover [topic…] [--horizon 1|2] [--provider p] [--limit n]", ""]],
|
|
537
567
|
note: "ranks by analyzing each candidate — this one takes minutes, not milliseconds.",
|
|
538
568
|
},
|
|
539
|
-
{ name: "tickers", description: "every ticker present in the index", synopsis: [["moshcode
|
|
540
|
-
{ name: "stats", description: "index coverage counts", synopsis: [["moshcode
|
|
541
|
-
{ name: "open", description: "open the shareable report page in a browser", synopsis: [["moshcode
|
|
569
|
+
{ name: "tickers", description: "every ticker present in the index", synopsis: [["moshcode stocks tickers", ""]] },
|
|
570
|
+
{ name: "stats", description: "index coverage counts", synopsis: [["moshcode stocks stats", ""]] },
|
|
571
|
+
{ name: "open", description: "open the shareable report page in a browser", synopsis: [["moshcode stocks open <symbol>", ""]] },
|
|
572
|
+
];
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* `crypto`'s verbs.
|
|
576
|
+
*
|
|
577
|
+
* `report` earns its place for the same reason stocks's does — a bare pair is
|
|
578
|
+
* the shortcut, so a pair that collides with a verb name needs a spelling that
|
|
579
|
+
* cannot be mistaken for one. src/crypto.mjs owns the parser and
|
|
580
|
+
* test/crypto.test.mjs fails when the two lists disagree.
|
|
581
|
+
*/
|
|
582
|
+
export const CRYPTO_VERBS = [
|
|
583
|
+
{ name: "report", description: "the full report for one pair", synopsis: [["moshcode crypto report <pair>", "same as `moshcode crypto <pair>`"]] },
|
|
584
|
+
{ name: "quote", description: "latest trade and quote, with the bid/ask spread", synopsis: [["moshcode crypto quote <pair>", ""]] },
|
|
585
|
+
{
|
|
586
|
+
name: "snapshot", description: "trade, quote and daily bars for several pairs",
|
|
587
|
+
synopsis: [["moshcode crypto snapshot <pair…>", "up to 20 pairs"]],
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "technicals", description: "indicators and the technical score",
|
|
591
|
+
synopsis: [["moshcode crypto technicals <pair> [--horizon 1|2]", ""]],
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
name: "bars", description: "historical OHLCV",
|
|
595
|
+
synopsis: [["moshcode crypto bars <pair> [--timeframe tf] [--start iso] [--end iso] [--limit n]", ""]],
|
|
596
|
+
},
|
|
597
|
+
{ name: "book", description: "top of the order book, both sides", synopsis: [["moshcode crypto book <pair> [--depth n]", ""]] },
|
|
598
|
+
{
|
|
599
|
+
name: "spark", description: "recent closes, drawn as sparklines",
|
|
600
|
+
synopsis: [["moshcode crypto spark <pair…> [--period 24h|7d]", ""]],
|
|
601
|
+
},
|
|
602
|
+
{ name: "assets", description: "every supported pair", synopsis: [["moshcode crypto assets", ""]] },
|
|
603
|
+
{
|
|
604
|
+
name: "lookup", description: "find a pair by asset name",
|
|
605
|
+
synopsis: [["moshcode crypto lookup <name…> [--limit n]", "bitcoin → BTC/USD"]],
|
|
606
|
+
},
|
|
607
|
+
{ name: "open", description: "open the shareable page in a browser", synopsis: [["moshcode crypto open <pair>", ""]] },
|
|
542
608
|
];
|
|
543
609
|
|
|
544
610
|
export const PLUGIN_VERBS = [
|
|
@@ -560,7 +626,8 @@ export const VERB_TABLES = {
|
|
|
560
626
|
UPGRADE_TARGETS,
|
|
561
627
|
DNS_VERBS,
|
|
562
628
|
TRADE_VERBS,
|
|
563
|
-
|
|
629
|
+
STOCKS_VERBS,
|
|
630
|
+
CRYPTO_VERBS,
|
|
564
631
|
PLUGIN_VERBS,
|
|
565
632
|
};
|
|
566
633
|
|
|
@@ -588,8 +655,10 @@ export const PIT_COMMANDS = [
|
|
|
588
655
|
description: "list workflow tools, or run one" },
|
|
589
656
|
{ name: "trade", args: "<verb> [args…]", cli: "trade",
|
|
590
657
|
description: "look up markets and preview/place Alpaca orders" },
|
|
591
|
-
{ name: "
|
|
658
|
+
{ name: "stocks", aliases: ["advisor"], args: "<symbol|verb> [args…]", cli: "stocks",
|
|
592
659
|
description: "equity research from advis0r.com" },
|
|
660
|
+
{ name: "crypto", aliases: ["coins"], args: "<pair|verb> [args…]", cli: "crypto",
|
|
661
|
+
description: "crypto market data from advis0r.com" },
|
|
593
662
|
{ name: "plugin", aliases: ["plugins"], args: "<verb> [name]", cli: "plugin",
|
|
594
663
|
description: "install moshcode's slash commands into Claude Code" },
|
|
595
664
|
{ name: "socials", aliases: ["social"], pitOnly: true,
|