diff --git a/README.md b/README.md index 91abf4b..2370631 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,14 @@ score that must not be ranked against an equity's. Restart the engine after installing either; a newly installed plugin is not live in a session that is already running. +Updating an installed plugin is its own step, and upgrading moshcode does not do +it: an engine only pulls a new copy when the *plugin's* own version moves. + +```sh +claude plugin update stocks@moshcode +claude plugin update crypto@moshcode +``` + Plugin commands are namespaced `/:` — always, not only when two plugins collide — so it is `/stocks:signals AAPL`, and a bare `/signals` answers `Unknown command`. Typing `/` and picking from the menu inserts the right form. diff --git a/plugins/crypto/.claude-plugin/plugin.json b/plugins/crypto/.claude-plugin/plugin.json index 8b31c56..68f715e 100644 --- a/plugins/crypto/.claude-plugin/plugin.json +++ b/plugins/crypto/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://anthropic.com/claude-code/plugin.schema.json", "name": "crypto", "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.", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "moshcoder", "url": "https://moshcode.sh" diff --git a/plugins/stocks/.claude-plugin/plugin.json b/plugins/stocks/.claude-plugin/plugin.json index 26bd2ac..4470ade 100644 --- a/plugins/stocks/.claude-plugin/plugin.json +++ b/plugins/stocks/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://anthropic.com/claude-code/plugin.schema.json", "name": "stocks", "description": "Equity research slash commands backed by advis0r.com: scored reports, extracted signals, transcript search, company-name lookup, and ranked watchlists.", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "moshcoder", "url": "https://moshcode.sh" diff --git a/src/plugins.mjs b/src/plugins.mjs index 3c51f48..817bb2f 100644 --- a/src/plugins.mjs +++ b/src/plugins.mjs @@ -39,16 +39,28 @@ export function marketplaceSource(env = process.env) { * `example` exists because the invitation printed after an install has to be * runnable. It used to append a hardcoded "NVDA" to whatever came first in * `commands`, which told anyone installing the crypto plugin to try a stock. + * + * `version` mirrors each plugin's own plugin.json, and it is not decoration: + * "If set, users only receive updates when you bump this field." + * https://code.claude.com/docs/en/plugins-reference#version-management + * + * So editing a plugin's commands and shipping a moshcode release is *not* + * enough — an existing install keeps serving the old copy until this number + * moves. Both plugins sat at 0.1.0 through v0.29.2, which rewrote every command + * file. Change a plugin's contents, bump its version, in the same commit. + * A test fails when this list and the manifests disagree. */ export const PLUGINS = [ { name: "stocks", + version: "0.2.0", description: "equity research slash commands backed by advis0r.com", commands: ["/stocks:stocks", "/stocks:signals", "/stocks:research", "/stocks:lookup", "/stocks:reports", "/stocks:discover"], example: "/stocks:stocks NVDA", }, { name: "crypto", + version: "0.2.0", description: "crypto market data slash commands backed by advis0r.com", commands: ["/crypto:crypto", "/crypto:quote", "/crypto:book", "/crypto:bars", "/crypto:spark", "/crypto:pairs", "/crypto:coin"], example: "/crypto:crypto BTC", diff --git a/test/plugins.test.mjs b/test/plugins.test.mjs index 9a808f0..59e729a 100644 --- a/test/plugins.test.mjs +++ b/test/plugins.test.mjs @@ -143,6 +143,17 @@ test("every plugin the marketplace lists exists, with a manifest and its command const plugin = JSON.parse(fs.readFileSync(new URL(".claude-plugin/plugin.json", dir), "utf8")); assert.equal(plugin.name, entry.name, `${entry.name}'s manifest disagrees with the marketplace`); + // An engine only pulls a new copy of a plugin when its own version moves — + // a moshcode release does not carry plugin edits to existing installs on its + // own. Both plugins sat at 0.1.0 through v0.29.2, which rewrote every + // command file, so this pins the catalog to the manifest that ships. + assert.match(plugin.version ?? "", /^\d+\.\d+\.\d+$/, `${entry.name} has no usable plugin.json version`); + assert.equal( + PLUGINS.find((p) => p.name === entry.name)?.version, + plugin.version, + `${entry.name}: the catalog and plugin.json disagree on the version`, + ); + const catalog = PLUGINS.find((p) => p.name === entry.name); const files = fs.readdirSync(new URL("commands/", dir)).filter((f) => f.endsWith(".md")); // Namespaced, because that is how Claude Code invokes them. Comparing bare