moshcode 0.29.1 → 0.29.3

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
@@ -376,18 +376,33 @@ moshcode plugin install crypto # add the marketplace + install `crypto`
376
376
  moshcode plugin remove stocks # take it back off
377
377
  ```
378
378
 
379
- `stocks@moshcode` adds `/stocks`, `/signals`, `/research`, `/lookup`,
380
- `/reports`, and `/discover` — the same advis0r research surface described above,
379
+ `stocks@moshcode` adds `/stocks:stocks`, `/stocks:signals`, `/stocks:research`,
380
+ `/stocks:lookup`, `/stocks:reports`, and `/stocks:discover` — the same advis0r research surface described above,
381
381
  driven from inside a coding session.
382
382
 
383
- `crypto@moshcode` adds `/crypto`, `/quote`, `/book`, `/bars`, `/spark`,
384
- `/pairs`, and `/coin`. It ships separately because it is a different surface,
383
+ `crypto@moshcode` adds `/crypto:crypto`, `/crypto:quote`, `/crypto:book`,
384
+ `/crypto:bars`, `/crypto:spark`, `/crypto:pairs`, and `/crypto:coin`. It ships separately because it is a different surface,
385
385
  not a mode of the first: live venue reads instead of stored snapshots, and a
386
386
  score that must not be ranked against an equity's.
387
387
 
388
388
  Restart the engine after installing either; a newly installed plugin is not live
389
389
  in a session that is already running.
390
390
 
391
+ Updating an installed plugin is its own step, and upgrading moshcode does not do
392
+ it: an engine only pulls a new copy when the *plugin's* own version moves.
393
+
394
+ ```sh
395
+ claude plugin update stocks@moshcode
396
+ claude plugin update crypto@moshcode
397
+ ```
398
+
399
+ Plugin commands are namespaced `/<plugin>:<command>` — always, not only when two
400
+ plugins collide — so it is `/stocks:signals AAPL`, and a bare `/signals` answers
401
+ `Unknown command`. Typing `/` and picking from the menu inserts the right form.
402
+ This is the one place the two surfaces differ: inside the moshcode pit the same
403
+ research is plain `/stocks …` and `/crypto …`, because those are moshcode's own
404
+ commands rather than a plugin's.
405
+
391
406
  The equivalent by hand:
392
407
 
393
408
  ```sh
@@ -405,7 +420,7 @@ any marketplace". `moshcode plugin install` runs both steps for you.
405
420
  ### Upgrading from `ticker@moshcode`
406
421
 
407
422
  `stocks` was called `ticker` before v0.29.0. Installing the new id does **not**
408
- replace the old one — engines install plugins side by side, so `/stocks` would
423
+ replace the old one — engines install plugins side by side, so `/stocks:stocks` would
409
424
  come from two plugins at once. Remove the old id first:
410
425
 
411
426
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moshcode",
3
- "version": "0.29.1",
3
+ "version": "0.29.3",
4
4
  "type": "module",
5
5
  "description": "moshcode — a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "crypto",
4
4
  "description": "Crypto market data slash commands backed by advis0r.com: live prices, technical scores, order books, OHLCV history and sparklines across Alpaca's US crypto venue.",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "author": {
7
7
  "name": "moshcoder",
8
8
  "url": "https://moshcode.sh"
@@ -6,17 +6,22 @@ across Alpaca's US crypto venue.
6
6
 
7
7
  | command | what it does |
8
8
  | --- | --- |
9
- | `/crypto BTC` | price, technicals, score, supply, order book |
10
- | `/quote ETH-USD` | latest trade and quote, with the spread in bps |
11
- | `/book BTC-USD` | top of the order book, both sides |
12
- | `/bars ETH-USD` | historical OHLCV at any supported timeframe |
13
- | `/spark BTC ETH SOL` | recent moves across pairs, ranked |
14
- | `/pairs` | every supported pair, grouped by quote asset |
15
- | `/coin bitcoin` | asset name → `BTC/USD` |
9
+ | `/crypto:crypto BTC` | price, technicals, score, supply, order book |
10
+ | `/crypto:quote ETH-USD` | latest trade and quote, with the spread in bps |
11
+ | `/crypto:book BTC-USD` | top of the order book, both sides |
12
+ | `/crypto:bars ETH-USD` | historical OHLCV at any supported timeframe |
13
+ | `/crypto:spark BTC ETH SOL` | recent moves across pairs, ranked |
14
+ | `/crypto:pairs` | every supported pair, grouped by quote asset |
15
+ | `/crypto:coin bitcoin` | asset name → `BTC/USD` |
16
16
 
17
17
  Pairs are accepted as `BTC`, `BTC-USD`, `BTC/USD` or `BTCUSD`. A bare asset
18
18
  resolves to that asset's USD pair.
19
19
 
20
+ The `crypto:` prefix is not optional. Claude Code namespaces every plugin
21
+ command as `/<plugin>:<command>` — always, not only when two plugins collide —
22
+ so a bare `/crypto` answers `Unknown command`. Typing `/` and picking from the
23
+ menu inserts the right form for you.
24
+
20
25
  ## Install
21
26
 
22
27
  ```bash
@@ -45,9 +50,9 @@ Point the commands at another instance with `MOSHCODE_ADVISOR_URL`.
45
50
  ## Why this is separate from `stocks`
46
51
 
47
52
  They answer different questions from different data, and share only a hostname.
48
- A `/stocks` report is a **stored snapshot** built from transcripts, SEC
53
+ A `/stocks:stocks` report is a **stored snapshot** built from transcripts, SEC
49
54
  fundamentals and extracted signals — its risk is a stale price read as a live
50
- one. A `/crypto` report is a **live venue read** with no transcripts, no
55
+ one. A `/crypto:crypto` report is a **live venue read** with no transcripts, no
51
56
  filings and no signals — its risk is the opposite: a price that is accurate to
52
57
  the second and stale by the time you act on it.
53
58
 
@@ -28,4 +28,4 @@ Fallback: `curl -sS "https://advis0r.com/api/crypto/lookup?q=<url-encoded>&limit
28
28
  match is the one they meant.
29
29
  - No match: say this venue lists no such pair, and do not invent a symbol. The
30
30
  coin may exist and simply not be listed here — those are different answers.
31
- - Offer `/crypto <PAIR>` for the match you land on.
31
+ - Offer `/crypto:crypto <PAIR>` for the match you land on.
@@ -42,9 +42,9 @@ resolves to that asset's USD pair.
42
42
  - Crypto trades 24/7 with no circuit breakers and no market close. Never
43
43
  describe a crypto price as "at the close" or "premarket".
44
44
  - State `generatedAt` / `fetchedAt`. This is a live read, so it goes stale in
45
- seconds, not days — the opposite failure mode from a stored `/stocks` report.
45
+ seconds, not days — the opposite failure mode from a stored `/stocks:stocks` report.
46
46
  - The score's liquidity component counts venue-local volume only, so it is
47
- **not comparable** to an equity's score from `/stocks`. Do not rank the two
47
+ **not comparable** to an equity's score from `/stocks:stocks`. Do not rank the two
48
48
  against each other.
49
49
  - End with the response's own `disclaimer`. This is research, not advice.
50
50
  - Link the shareable page: `https://advis0r.com/crypto/<PAIR>`.
@@ -25,7 +25,7 @@ URL-safe spelling (`BTC-USD`) used in paths; `symbol` is canonical (`BTC/USD`).
25
25
  different markets with three different liquidity profiles.
26
26
  - **`idle` means listed but not currently printing trades** — it is not the same
27
27
  as unsupported. Show idle pairs, marked, rather than filtering them out.
28
- - If the user was looking for a specific coin, use `/coin <name>` instead of
28
+ - If the user was looking for a specific coin, use `/crypto:coin <name>` instead of
29
29
  scanning this list for them.
30
30
  - This is the coverage of one venue, not of crypto. A coin missing here is
31
31
  missing *from Alpaca's US venue*.
@@ -32,5 +32,5 @@ Fallback: `curl -sS "https://advis0r.com/api/crypto/sparklines?symbols=BTC-USD,E
32
32
  - Name the window (`start` → `end`) and the period. "Up 5%" over 24h and over 7d
33
33
  are different claims.
34
34
  - Do not extrapolate a trend from 24 points, and do not call a direction
35
- "momentum" without the technicals to back it — `/crypto <PAIR>` has those.
35
+ "momentum" without the technicals to back it — `/crypto:crypto <PAIR>` has those.
36
36
  - End with the response's own `disclaimer`.
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "stocks",
4
4
  "description": "Equity research slash commands backed by advis0r.com: scored reports, extracted signals, transcript search, company-name lookup, and ranked watchlists.",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "author": {
7
7
  "name": "moshcoder",
8
8
  "url": "https://moshcode.sh"
@@ -6,21 +6,29 @@ and ranked watchlists.
6
6
 
7
7
  | command | what it does |
8
8
  | --- | --- |
9
- | `/stocks NVDA` | score, technicals, fundamentals, thesis, signals, sources |
10
- | `/signals AAPL` | what was actually said, quoted and sourced |
11
- | `/research data center` | full-text search across every indexed transcript |
12
- | `/lookup rivian` | company name → `RIVN` |
13
- | `/reports` | every stored report, best score first |
14
- | `/discover fusion` | a ranked watchlist for a topic (slow) |
9
+ | `/stocks:stocks NVDA` | score, technicals, fundamentals, thesis, signals, sources |
10
+ | `/stocks:signals AAPL` | what was actually said, quoted and sourced |
11
+ | `/stocks:research data center` | full-text search across every indexed transcript |
12
+ | `/stocks:lookup rivian` | company name → `RIVN` |
13
+ | `/stocks:reports` | every stored report, best score first |
14
+ | `/stocks:discover fusion` | a ranked watchlist for a topic (slow) |
15
+
16
+ The `stocks:` prefix is not optional. Claude Code namespaces every plugin
17
+ command as `/<plugin>:<command>` — always, not only when two plugins collide —
18
+ so a bare `/stocks` answers `Unknown command`. Typing `/` and picking from the
19
+ menu inserts the right form for you.
20
+
21
+ (Inside the moshcode pit itself, `/stocks …` *is* bare — that is moshcode's own
22
+ command, not this plugin's.)
15
23
 
16
24
  ## Upgrading from `ticker@moshcode`
17
25
 
18
26
  This plugin used to be called `ticker`. Both it and its headline command were
19
- renamed so the name says which market it covers, now that `/crypto` sits beside
27
+ renamed so the name says which market it covers, now that `/crypto:crypto` sits beside
20
28
  it.
21
29
 
22
30
  Installing the new one does **not** replace the old one — engines install
23
- plugins side by side, so `/stocks` would come from two plugins at once. Remove
31
+ plugins side by side, so `/stocks:stocks` would come from two plugins at once. Remove
24
32
  the old id first:
25
33
 
26
34
  ```bash
@@ -22,6 +22,6 @@ Fallback: `curl -sS "https://advis0r.com/api/lookup?q=<url-encoded>&limit=10"`
22
22
  ## Rules
23
23
 
24
24
  - Show every match with its exchange — "Delta" is an airline and a faucet company.
25
- - Mark which ones have a report, and offer `/stocks <SYMBOL>` for those.
25
+ - Mark which ones have a report, and offer `/stocks:stocks <SYMBOL>` for those.
26
26
  - One unambiguous match: say the symbol and go straight to offering the report.
27
27
  - No match: say the *directory* has no match, and do not invent a symbol.
@@ -26,5 +26,5 @@ generatedAt }`, and `total` is how many exist.
26
26
  - **`generatedAt` per row, always.** These are snapshots taken at different
27
27
  times; a table that hides that reads as one consistent as-of date.
28
28
  - A row with no `aiProvider` was scored deterministically, not by a model.
29
- - Offer `/stocks <SYMBOL>` for anything worth a closer look.
29
+ - Offer `/stocks:stocks <SYMBOL>` for anything worth a closer look.
30
30
  - This is a coverage list, not a recommendation list. Rank order is score order.
@@ -25,5 +25,5 @@ hit is a hit — but relevance is not ranked. Read before summarizing.
25
25
  - Cluster the hits by ticker and say which companies came up, with dates.
26
26
  - Quote sparingly and attribute each quote to its speaker and ticker.
27
27
  - If nothing matches, say the *index* has no match — this searches advis0r's
28
- indexed corpus, not the whole web. Suggest `/lookup` if the query looks like
28
+ indexed corpus, not the whole web. Suggest `/stocks:lookup` if the query looks like
29
29
  a company name.
package/src/crypto.mjs CHANGED
@@ -488,7 +488,11 @@ function priceLine(snapshot, quote) {
488
488
  const bits = [bone(price(last, quote))];
489
489
  if (change) {
490
490
  const paint = changeTone(change.percent);
491
- bits.push(paint(`${change.absolute >= 0 ? "+" : ""}${price(change.absolute, quote, { like: last })}`), paint(`(${pct(change.percent)})`));
491
+ // Sign the magnitude, don't let price() sign it: a signed price puts the
492
+ // minus after the currency mark ("$-186.36"), so a down move reads unlike
493
+ // the up move's "+$186.36". The sign leads, the way pct() already signs.
494
+ const signed = `${change.absolute >= 0 ? "+" : "-"}${price(Math.abs(Number(change.absolute)), quote, { like: last })}`;
495
+ bits.push(paint(signed), paint(`(${pct(change.percent)})`));
492
496
  }
493
497
  const feed = [
494
498
  snapshot?.delayed === false ? "live" : snapshot?.delayed === true ? "delayed" : null,
@@ -345,7 +345,7 @@ export async function pluginCommand(tokens, { run, installedSet } = {}) {
345
345
  // A newly installed plugin is not live in an already-running engine, and the
346
346
  // first thing anyone does is type the slash command and conclude it failed.
347
347
  if (!anyFailed(results) && verb === "install" && results.some((r) => r.status === "installed")) {
348
- console.log(info(`restart the engine, then try ${acid(`${plugin.commands[0]} NVDA`)}`));
348
+ console.log(info(`restart the engine, then try ${acid(plugin.example ?? plugin.commands[0])}`));
349
349
  }
350
350
  return anyFailed(results) ? 1 : 0;
351
351
  }
package/src/plugins.mjs CHANGED
@@ -26,20 +26,52 @@ export function marketplaceSource(env = process.env) {
26
26
  return String(env.MOSHCODE_PLUGIN_SOURCE || "moshcoder/moshcode").trim() || "moshcoder/moshcode";
27
27
  }
28
28
 
29
- /** The plugins this marketplace ships. Mirrors .claude-plugin/marketplace.json. */
29
+ /**
30
+ * The plugins this marketplace ships. Mirrors .claude-plugin/marketplace.json.
31
+ *
32
+ * `commands` carry their namespace because that is how they are actually
33
+ * invoked. Claude Code namespaces every plugin command as
34
+ * `/<plugin>:<command>` — always, not only when two plugins collide — so a bare
35
+ * `/crypto` is simply not a command, and advertising one sends people to
36
+ * "Unknown command: /crypto" on their first try.
37
+ * https://code.claude.com/docs/en/plugins
38
+ *
39
+ * `example` exists because the invitation printed after an install has to be
40
+ * runnable. It used to append a hardcoded "NVDA" to whatever came first in
41
+ * `commands`, which told anyone installing the crypto plugin to try a stock.
42
+ *
43
+ * `version` mirrors each plugin's own plugin.json, and it is not decoration:
44
+ * "If set, users only receive updates when you bump this field."
45
+ * https://code.claude.com/docs/en/plugins-reference#version-management
46
+ *
47
+ * So editing a plugin's commands and shipping a moshcode release is *not*
48
+ * enough — an existing install keeps serving the old copy until this number
49
+ * moves. Both plugins sat at 0.1.0 through v0.29.2, which rewrote every command
50
+ * file. Change a plugin's contents, bump its version, in the same commit.
51
+ * A test fails when this list and the manifests disagree.
52
+ */
30
53
  export const PLUGINS = [
31
54
  {
32
55
  name: "stocks",
56
+ version: "0.2.0",
33
57
  description: "equity research slash commands backed by advis0r.com",
34
- commands: ["/stocks", "/signals", "/research", "/lookup", "/reports", "/discover"],
58
+ commands: ["/stocks:stocks", "/stocks:signals", "/stocks:research", "/stocks:lookup", "/stocks:reports", "/stocks:discover"],
59
+ example: "/stocks:stocks NVDA",
35
60
  },
36
61
  {
37
62
  name: "crypto",
63
+ version: "0.2.0",
38
64
  description: "crypto market data slash commands backed by advis0r.com",
39
- commands: ["/crypto", "/quote", "/book", "/bars", "/spark", "/pairs", "/coin"],
65
+ commands: ["/crypto:crypto", "/crypto:quote", "/crypto:book", "/crypto:bars", "/crypto:spark", "/crypto:pairs", "/crypto:coin"],
66
+ example: "/crypto:crypto BTC",
40
67
  },
41
68
  ];
42
69
 
70
+ /** How Claude Code namespaces a plugin's command. */
71
+ export function pluginCommandName(plugin, file) {
72
+ return `/${plugin}:${String(file).replace(/\.md$/, "")}`;
73
+ }
74
+
43
75
  export const DEFAULT_PLUGIN = PLUGINS[0].name;
44
76
 
45
77
  /**