@ryuhq/sdk 0.1.3 → 0.1.5

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.
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/agent-plugin.ts
21
+ var agent_plugin_exports = {};
22
+ __export(agent_plugin_exports, {
23
+ AGENT_PLUGINS_SPEC_VERSION: () => AGENT_PLUGINS_SPEC_VERSION,
24
+ AGENT_PLUGIN_EXTENSION_NS: () => AGENT_PLUGIN_EXTENSION_NS,
25
+ AGENT_PLUGIN_MANIFEST_FILE: () => AGENT_PLUGIN_MANIFEST_FILE,
26
+ AGENT_PLUGIN_MCP_FILE: () => AGENT_PLUGIN_MCP_FILE,
27
+ AGENT_PLUGIN_MCP_SCHEMA_URL: () => AGENT_PLUGIN_MCP_SCHEMA_URL,
28
+ AGENT_PLUGIN_SCHEMA_URL: () => AGENT_PLUGIN_SCHEMA_URL,
29
+ isAgentPluginManifest: () => isAgentPluginManifest,
30
+ toAgentPlugin: () => toAgentPlugin,
31
+ toSpecName: () => toSpecName
32
+ });
33
+ module.exports = __toCommonJS(agent_plugin_exports);
34
+ var AGENT_PLUGINS_SPEC_VERSION = "1.0.0";
35
+ var AGENT_PLUGIN_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
36
+ var AGENT_PLUGIN_MCP_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
37
+ var AGENT_PLUGIN_EXTENSION_NS = "com.ryuhq.ryu";
38
+ var AGENT_PLUGIN_MANIFEST_FILE = "plugin.json";
39
+ var AGENT_PLUGIN_MCP_FILE = "mcp.json";
40
+ function isAgentPluginManifest(value) {
41
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
42
+ return false;
43
+ }
44
+ const schema = value.$schema;
45
+ return typeof schema === "string" && schema.startsWith("https://agent-plugins.org/schemas/");
46
+ }
47
+ var SPEC_NAME_MAX = 64;
48
+ var ILLEGAL_NAME_CHARS = /[^a-z0-9.-]+/g;
49
+ var REPEATED_HYPHENS = /-{2,}/g;
50
+ var REPEATED_DOTS = /\.{2,}/g;
51
+ var LEADING_NON_ALNUM = /^[^a-z0-9]+/;
52
+ var TRAILING_NON_ALNUM = /[^a-z0-9]+$/;
53
+ var WHITESPACE = /\s/;
54
+ function toSpecName(id) {
55
+ const normalized = id.trim().toLowerCase().replace(/^@/, "").replace(/[/_]/g, ".").replace(ILLEGAL_NAME_CHARS, "-").replace(REPEATED_HYPHENS, "-").replace(REPEATED_DOTS, ".").replace(LEADING_NON_ALNUM, "").replace(TRAILING_NON_ALNUM, "").slice(0, SPEC_NAME_MAX).replace(TRAILING_NON_ALNUM, "");
56
+ if (!normalized) {
57
+ throw new Error(
58
+ `plugin id ${JSON.stringify(id)} has no spec-legal name projection`
59
+ );
60
+ }
61
+ return normalized;
62
+ }
63
+ function asString(value) {
64
+ return typeof value === "string" && value.trim() ? value : void 0;
65
+ }
66
+ function asStringArray(value) {
67
+ if (!Array.isArray(value)) {
68
+ return;
69
+ }
70
+ const strings = value.filter((v) => typeof v === "string");
71
+ return strings.length > 0 ? strings : void 0;
72
+ }
73
+ function toSpecAuthor(value) {
74
+ const bare = asString(value);
75
+ if (bare) {
76
+ return { name: bare };
77
+ }
78
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
79
+ return;
80
+ }
81
+ const source = value;
82
+ const author = {};
83
+ const name = asString(source.name);
84
+ const email = asString(source.email);
85
+ const url = asString(source.url);
86
+ if (name) {
87
+ author.name = name;
88
+ }
89
+ if (email) {
90
+ author.email = email;
91
+ }
92
+ if (url) {
93
+ author.url = url;
94
+ }
95
+ return Object.keys(author).length > 0 ? author : void 0;
96
+ }
97
+ function toSpecEnv(value) {
98
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
99
+ return;
100
+ }
101
+ const env = {};
102
+ for (const [key, raw] of Object.entries(value)) {
103
+ if (typeof raw === "string") {
104
+ env[key] = raw;
105
+ }
106
+ }
107
+ return Object.keys(env).length > 0 ? env : void 0;
108
+ }
109
+ function toSpecServer(name, decl) {
110
+ const extras = {};
111
+ const commandEnv = asString(decl.command_env);
112
+ const description = asString(decl.description);
113
+ if (commandEnv) {
114
+ extras.command_env = commandEnv;
115
+ }
116
+ if (description) {
117
+ extras.description = description;
118
+ }
119
+ if (decl.enabled === false) {
120
+ extras.enabled = false;
121
+ return {
122
+ extras,
123
+ note: `mcp server '${name}' is disabled in the native manifest and was omitted from ${AGENT_PLUGIN_MCP_FILE}`
124
+ };
125
+ }
126
+ const command = asString(decl.command);
127
+ if (!command) {
128
+ return {
129
+ extras,
130
+ note: `mcp server '${name}' has no command and was omitted`
131
+ };
132
+ }
133
+ if (WHITESPACE.test(command)) {
134
+ return {
135
+ extras,
136
+ note: `mcp server '${name}' command ${JSON.stringify(command)} is not a single executable token and was omitted`
137
+ };
138
+ }
139
+ if (command.startsWith("/") || command.startsWith("~")) {
140
+ return {
141
+ extras,
142
+ note: `mcp server '${name}' command ${JSON.stringify(command)} is an absolute path (spec allows a bare name or './' relative path) and was omitted`
143
+ };
144
+ }
145
+ const server = { type: "stdio", command };
146
+ const args = asStringArray(decl.args);
147
+ if (args) {
148
+ server.args = args;
149
+ }
150
+ const env = toSpecEnv(decl.env);
151
+ if (env) {
152
+ server.env = env;
153
+ }
154
+ return { server, extras };
155
+ }
156
+ function toAgentPlugin(manifest) {
157
+ const id = asString(manifest.id);
158
+ if (!id) {
159
+ throw new Error("manifest has no id");
160
+ }
161
+ const notes = [];
162
+ const ryu = {
163
+ id,
164
+ displayName: asString(manifest.name) ?? id
165
+ };
166
+ const plugin = {
167
+ $schema: AGENT_PLUGIN_SCHEMA_URL,
168
+ name: toSpecName(id)
169
+ };
170
+ const version = asString(manifest.version);
171
+ if (version) {
172
+ plugin.version = version;
173
+ }
174
+ const description = asString(manifest.description) ?? asString(manifest.tagline);
175
+ if (description) {
176
+ plugin.description = description;
177
+ }
178
+ const author = toSpecAuthor(manifest.author);
179
+ if (author) {
180
+ plugin.author = author;
181
+ }
182
+ const homepage = asString(manifest.homepage);
183
+ if (homepage) {
184
+ plugin.homepage = homepage;
185
+ }
186
+ const repository = asString(manifest.repository);
187
+ if (repository) {
188
+ plugin.repository = repository;
189
+ }
190
+ const license = asString(manifest.license);
191
+ if (license) {
192
+ plugin.license = license;
193
+ }
194
+ const keywords = asStringArray(manifest.keywords);
195
+ if (keywords) {
196
+ plugin.keywords = keywords;
197
+ }
198
+ const declared = manifest.mcp_servers;
199
+ const servers = {};
200
+ const mcpExtras = {};
201
+ if (declared && typeof declared === "object" && !Array.isArray(declared)) {
202
+ for (const [name, raw] of Object.entries(
203
+ declared
204
+ )) {
205
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
206
+ notes.push(`mcp server '${name}' is not an object and was omitted`);
207
+ continue;
208
+ }
209
+ const { server, extras, note } = toSpecServer(
210
+ name,
211
+ raw
212
+ );
213
+ if (Object.keys(extras).length > 0) {
214
+ mcpExtras[name] = extras;
215
+ }
216
+ if (note) {
217
+ notes.push(note);
218
+ }
219
+ if (server) {
220
+ servers[name] = server;
221
+ }
222
+ }
223
+ }
224
+ if (Object.keys(mcpExtras).length > 0) {
225
+ ryu.mcp = mcpExtras;
226
+ }
227
+ plugin.extensions = { [AGENT_PLUGIN_EXTENSION_NS]: ryu };
228
+ const mcp = Object.keys(servers).length > 0 ? { $schema: AGENT_PLUGIN_MCP_SCHEMA_URL, mcpServers: servers } : null;
229
+ return { plugin, mcp, notes };
230
+ }
231
+ // Annotate the CommonJS export names for ESM import in node:
232
+ 0 && (module.exports = {
233
+ AGENT_PLUGINS_SPEC_VERSION,
234
+ AGENT_PLUGIN_EXTENSION_NS,
235
+ AGENT_PLUGIN_MANIFEST_FILE,
236
+ AGENT_PLUGIN_MCP_FILE,
237
+ AGENT_PLUGIN_MCP_SCHEMA_URL,
238
+ AGENT_PLUGIN_SCHEMA_URL,
239
+ isAgentPluginManifest,
240
+ toAgentPlugin,
241
+ toSpecName
242
+ });
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Agent Plugins v1.0.0 export — the interop face of a Ryu `manifest.json`.
3
+ *
4
+ * The Agent Plugins Specification (https://agent-plugins.org/, TSC: Amazon,
5
+ * Cursor, Microsoft, OpenAI, Vercel) defines a small portable floor: a plugin is
6
+ * a directory with `plugin.json`, Agent Skills under `skills/<slug>/SKILL.md`,
7
+ * and MCP servers in `mcp.json`. Nothing else is portable.
8
+ *
9
+ * ## Why this is a SECOND file, not a migration
10
+ *
11
+ * The spec manifest schema is **closed** (§5.2): the only permitted top-level
12
+ * fields are `$schema`, `name`, `version`, `description`, `author`, `homepage`,
13
+ * `repository`, `license`, `keywords`, and `extensions`. Every field that makes a
14
+ * Ryu manifest a Ryu manifest — `id`, `runnables`, `contributes`, `surfaces`,
15
+ * `engines`, `permission_grants`, `mcp_servers`, `companion`, `ui_code_sha256` —
16
+ * is an unknown field there. So `manifest.json` can never *be* a conformant
17
+ * `plugin.json`; it can only be projected into one.
18
+ *
19
+ * `manifest.json` therefore stays the single source of truth and this module
20
+ * DERIVES the interop pair (`plugin.json` + `mcp.json`) from it. Nothing is
21
+ * hand-maintained, so the pair cannot desync — the same reason the packaged
22
+ * manifests are compiled in from their package home instead of copied (AGENTS.md).
23
+ *
24
+ * For the same reason `extensions` carries only what cannot be re-derived by a
25
+ * reader of the spec files: the real scoped id, the display name, and the
26
+ * per-server MCP fields the spec's closed server variants forced us to strip. It
27
+ * is deliberately NOT a copy of the whole native manifest — that would be a second
28
+ * source of truth with a stale-copy failure mode.
29
+ *
30
+ * Both `plugins-store/*` and `apps-store/*` use this one `PluginManifest` shape, so
31
+ * one converter covers both stores.
32
+ */
33
+ /** Agent Plugins spec version this module targets. */
34
+ declare const AGENT_PLUGINS_SPEC_VERSION = "1.0.0";
35
+ /**
36
+ * Canonical manifest schema identifier (§5.2). MUST be this exact string — a
37
+ * client selects its validation rules from the value and MUST NOT fetch it.
38
+ */
39
+ declare const AGENT_PLUGIN_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
40
+ /** Canonical `mcp.json` schema identifier (§7.2.1). */
41
+ declare const AGENT_PLUGIN_MCP_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
42
+ /**
43
+ * Our reverse-domain client extension namespace (§8) — the key in `extensions`
44
+ * AND, when a plugin ships Ryu-only files, the top-level directory name.
45
+ *
46
+ * The spec asks for a domain the client controls, kept stable indefinitely, so
47
+ * this is a one-way door: changing it later orphans every published plugin's Ryu
48
+ * data. Derived from the `@ryuhq` npm scope / `ryuhq.com`.
49
+ */
50
+ declare const AGENT_PLUGIN_EXTENSION_NS = "com.ryuhq.ryu";
51
+ /** Spec file name for the manifest (§5.1). */
52
+ declare const AGENT_PLUGIN_MANIFEST_FILE = "plugin.json";
53
+ /** Spec file name for the MCP configuration (§7.2.1). */
54
+ declare const AGENT_PLUGIN_MCP_FILE = "mcp.json";
55
+ /**
56
+ * Whether a parsed JSON value is an Agent Plugins spec manifest rather than a
57
+ * native Ryu one.
58
+ *
59
+ * This predicate is load-bearing, not cosmetic. `plugin.json` is BOTH the spec's
60
+ * manifest name and a legacy alias for our own `manifest.json` (Core's
61
+ * `MANIFEST_FILE_NAMES` and the CLI's copy of it both still accept it). Once a
62
+ * plugin directory carries an exported spec `plugin.json`, any resolver that
63
+ * blindly takes the first matching name can pick the wrong file and reject the
64
+ * plugin for having no `id`/`runnables`.
65
+ *
66
+ * The discriminator is unambiguous: a spec manifest MUST carry `$schema` with the
67
+ * canonical agent-plugins.org identifier (§5.2), and no native manifest has ever
68
+ * had that field.
69
+ */
70
+ declare function isAgentPluginManifest(value: unknown): boolean;
71
+ /** Author object — the only three fields the spec permits (§5.4). */
72
+ type AgentPluginAuthor = {
73
+ name?: string;
74
+ email?: string;
75
+ url?: string;
76
+ };
77
+ /** Ryu data carried under {@link AGENT_PLUGIN_EXTENSION_NS}. */
78
+ type RyuExtensionData = {
79
+ /** The real scoped plugin id (`@ryu/advisor`) — unrecoverable from spec `name`. */
80
+ id: string;
81
+ /** Human display name; spec `name` is a slug, not a display string. */
82
+ displayName: string;
83
+ /** Per-MCP-server fields the spec's closed server variants do not allow. */
84
+ mcp?: Record<string, RyuMcpServerExtras>;
85
+ };
86
+ /** Native MCP fields stripped out of the exported `mcp.json`. */
87
+ type RyuMcpServerExtras = {
88
+ /** Env var that overrides `command` with an absolute path at spawn. */
89
+ command_env?: string;
90
+ /** Human description for our MCP listing endpoint. */
91
+ description?: string;
92
+ /**
93
+ * Present and `false` when the native manifest disables the server. Such a
94
+ * server is OMITTED from `mcp.json` entirely — the spec has no `enabled` flag,
95
+ * so emitting the entry would make a foreign client spawn something we
96
+ * deliberately do not.
97
+ */
98
+ enabled?: false;
99
+ };
100
+ /** A conformant `plugin.json` (§5.2). */
101
+ type AgentPluginJson = {
102
+ $schema: string;
103
+ name: string;
104
+ version?: string;
105
+ description?: string;
106
+ author?: AgentPluginAuthor;
107
+ homepage?: string;
108
+ repository?: string;
109
+ license?: string;
110
+ keywords?: string[];
111
+ extensions: Record<string, unknown>;
112
+ };
113
+ /** A stdio server entry (§7.2.1) — the only variant we export. */
114
+ type AgentPluginStdioServer = {
115
+ type: "stdio";
116
+ command: string;
117
+ args?: string[];
118
+ env?: Record<string, string>;
119
+ cwd?: string;
120
+ };
121
+ /** A conformant `mcp.json` (§7.2.1). */
122
+ type AgentPluginMcpJson = {
123
+ $schema: string;
124
+ mcpServers: Record<string, AgentPluginStdioServer>;
125
+ };
126
+ /** What {@link toAgentPlugin} produces, plus what it had to leave behind. */
127
+ type AgentPluginExport = {
128
+ /** The `plugin.json` contents. */
129
+ plugin: AgentPluginJson;
130
+ /** The `mcp.json` contents, or null when the plugin exports no server. */
131
+ mcp: AgentPluginMcpJson | null;
132
+ /**
133
+ * Human-readable notes about anything dropped or rewritten, so a lossy export
134
+ * is visible at the call site instead of silent.
135
+ */
136
+ notes: string[];
137
+ };
138
+ /**
139
+ * Project a Ryu plugin id onto a spec-legal `name` (§5.5): 1–64 chars of
140
+ * `a-z 0-9 - .`, alphanumeric at both ends, no `--` and no `..`.
141
+ *
142
+ * Our ids are all `@scope/name`, which is illegal there (`@` and `/`), so
143
+ * `@ryu/advisor` becomes `ryu.advisor`. Periods ARE legal, which is what makes the
144
+ * mapping readable rather than a hash. The mapping is lossy by construction (two
145
+ * ids could collide after normalization), so the true id always rides in
146
+ * `extensions` and this value is never treated as an identity on our side.
147
+ */
148
+ declare function toSpecName(id: string): string;
149
+ /**
150
+ * Project a Ryu `manifest.json` onto the Agent Plugins interop pair.
151
+ *
152
+ * Takes the RAW parsed manifest (not the SDK's narrower zod type) because the
153
+ * fields that matter for export — notably `mcp_servers` — live in Core's richer
154
+ * model. Throws only when the id cannot be projected onto a spec-legal name;
155
+ * every other lossy step is reported through {@link AgentPluginExport.notes}.
156
+ */
157
+ declare function toAgentPlugin(manifest: Record<string, unknown>): AgentPluginExport;
158
+
159
+ export { AGENT_PLUGINS_SPEC_VERSION, AGENT_PLUGIN_EXTENSION_NS, AGENT_PLUGIN_MANIFEST_FILE, AGENT_PLUGIN_MCP_FILE, AGENT_PLUGIN_MCP_SCHEMA_URL, AGENT_PLUGIN_SCHEMA_URL, type AgentPluginAuthor, type AgentPluginExport, type AgentPluginJson, type AgentPluginMcpJson, type AgentPluginStdioServer, type RyuExtensionData, type RyuMcpServerExtras, isAgentPluginManifest, toAgentPlugin, toSpecName };
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Agent Plugins v1.0.0 export — the interop face of a Ryu `manifest.json`.
3
+ *
4
+ * The Agent Plugins Specification (https://agent-plugins.org/, TSC: Amazon,
5
+ * Cursor, Microsoft, OpenAI, Vercel) defines a small portable floor: a plugin is
6
+ * a directory with `plugin.json`, Agent Skills under `skills/<slug>/SKILL.md`,
7
+ * and MCP servers in `mcp.json`. Nothing else is portable.
8
+ *
9
+ * ## Why this is a SECOND file, not a migration
10
+ *
11
+ * The spec manifest schema is **closed** (§5.2): the only permitted top-level
12
+ * fields are `$schema`, `name`, `version`, `description`, `author`, `homepage`,
13
+ * `repository`, `license`, `keywords`, and `extensions`. Every field that makes a
14
+ * Ryu manifest a Ryu manifest — `id`, `runnables`, `contributes`, `surfaces`,
15
+ * `engines`, `permission_grants`, `mcp_servers`, `companion`, `ui_code_sha256` —
16
+ * is an unknown field there. So `manifest.json` can never *be* a conformant
17
+ * `plugin.json`; it can only be projected into one.
18
+ *
19
+ * `manifest.json` therefore stays the single source of truth and this module
20
+ * DERIVES the interop pair (`plugin.json` + `mcp.json`) from it. Nothing is
21
+ * hand-maintained, so the pair cannot desync — the same reason the packaged
22
+ * manifests are compiled in from their package home instead of copied (AGENTS.md).
23
+ *
24
+ * For the same reason `extensions` carries only what cannot be re-derived by a
25
+ * reader of the spec files: the real scoped id, the display name, and the
26
+ * per-server MCP fields the spec's closed server variants forced us to strip. It
27
+ * is deliberately NOT a copy of the whole native manifest — that would be a second
28
+ * source of truth with a stale-copy failure mode.
29
+ *
30
+ * Both `plugins-store/*` and `apps-store/*` use this one `PluginManifest` shape, so
31
+ * one converter covers both stores.
32
+ */
33
+ /** Agent Plugins spec version this module targets. */
34
+ declare const AGENT_PLUGINS_SPEC_VERSION = "1.0.0";
35
+ /**
36
+ * Canonical manifest schema identifier (§5.2). MUST be this exact string — a
37
+ * client selects its validation rules from the value and MUST NOT fetch it.
38
+ */
39
+ declare const AGENT_PLUGIN_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
40
+ /** Canonical `mcp.json` schema identifier (§7.2.1). */
41
+ declare const AGENT_PLUGIN_MCP_SCHEMA_URL = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
42
+ /**
43
+ * Our reverse-domain client extension namespace (§8) — the key in `extensions`
44
+ * AND, when a plugin ships Ryu-only files, the top-level directory name.
45
+ *
46
+ * The spec asks for a domain the client controls, kept stable indefinitely, so
47
+ * this is a one-way door: changing it later orphans every published plugin's Ryu
48
+ * data. Derived from the `@ryuhq` npm scope / `ryuhq.com`.
49
+ */
50
+ declare const AGENT_PLUGIN_EXTENSION_NS = "com.ryuhq.ryu";
51
+ /** Spec file name for the manifest (§5.1). */
52
+ declare const AGENT_PLUGIN_MANIFEST_FILE = "plugin.json";
53
+ /** Spec file name for the MCP configuration (§7.2.1). */
54
+ declare const AGENT_PLUGIN_MCP_FILE = "mcp.json";
55
+ /**
56
+ * Whether a parsed JSON value is an Agent Plugins spec manifest rather than a
57
+ * native Ryu one.
58
+ *
59
+ * This predicate is load-bearing, not cosmetic. `plugin.json` is BOTH the spec's
60
+ * manifest name and a legacy alias for our own `manifest.json` (Core's
61
+ * `MANIFEST_FILE_NAMES` and the CLI's copy of it both still accept it). Once a
62
+ * plugin directory carries an exported spec `plugin.json`, any resolver that
63
+ * blindly takes the first matching name can pick the wrong file and reject the
64
+ * plugin for having no `id`/`runnables`.
65
+ *
66
+ * The discriminator is unambiguous: a spec manifest MUST carry `$schema` with the
67
+ * canonical agent-plugins.org identifier (§5.2), and no native manifest has ever
68
+ * had that field.
69
+ */
70
+ declare function isAgentPluginManifest(value: unknown): boolean;
71
+ /** Author object — the only three fields the spec permits (§5.4). */
72
+ type AgentPluginAuthor = {
73
+ name?: string;
74
+ email?: string;
75
+ url?: string;
76
+ };
77
+ /** Ryu data carried under {@link AGENT_PLUGIN_EXTENSION_NS}. */
78
+ type RyuExtensionData = {
79
+ /** The real scoped plugin id (`@ryu/advisor`) — unrecoverable from spec `name`. */
80
+ id: string;
81
+ /** Human display name; spec `name` is a slug, not a display string. */
82
+ displayName: string;
83
+ /** Per-MCP-server fields the spec's closed server variants do not allow. */
84
+ mcp?: Record<string, RyuMcpServerExtras>;
85
+ };
86
+ /** Native MCP fields stripped out of the exported `mcp.json`. */
87
+ type RyuMcpServerExtras = {
88
+ /** Env var that overrides `command` with an absolute path at spawn. */
89
+ command_env?: string;
90
+ /** Human description for our MCP listing endpoint. */
91
+ description?: string;
92
+ /**
93
+ * Present and `false` when the native manifest disables the server. Such a
94
+ * server is OMITTED from `mcp.json` entirely — the spec has no `enabled` flag,
95
+ * so emitting the entry would make a foreign client spawn something we
96
+ * deliberately do not.
97
+ */
98
+ enabled?: false;
99
+ };
100
+ /** A conformant `plugin.json` (§5.2). */
101
+ type AgentPluginJson = {
102
+ $schema: string;
103
+ name: string;
104
+ version?: string;
105
+ description?: string;
106
+ author?: AgentPluginAuthor;
107
+ homepage?: string;
108
+ repository?: string;
109
+ license?: string;
110
+ keywords?: string[];
111
+ extensions: Record<string, unknown>;
112
+ };
113
+ /** A stdio server entry (§7.2.1) — the only variant we export. */
114
+ type AgentPluginStdioServer = {
115
+ type: "stdio";
116
+ command: string;
117
+ args?: string[];
118
+ env?: Record<string, string>;
119
+ cwd?: string;
120
+ };
121
+ /** A conformant `mcp.json` (§7.2.1). */
122
+ type AgentPluginMcpJson = {
123
+ $schema: string;
124
+ mcpServers: Record<string, AgentPluginStdioServer>;
125
+ };
126
+ /** What {@link toAgentPlugin} produces, plus what it had to leave behind. */
127
+ type AgentPluginExport = {
128
+ /** The `plugin.json` contents. */
129
+ plugin: AgentPluginJson;
130
+ /** The `mcp.json` contents, or null when the plugin exports no server. */
131
+ mcp: AgentPluginMcpJson | null;
132
+ /**
133
+ * Human-readable notes about anything dropped or rewritten, so a lossy export
134
+ * is visible at the call site instead of silent.
135
+ */
136
+ notes: string[];
137
+ };
138
+ /**
139
+ * Project a Ryu plugin id onto a spec-legal `name` (§5.5): 1–64 chars of
140
+ * `a-z 0-9 - .`, alphanumeric at both ends, no `--` and no `..`.
141
+ *
142
+ * Our ids are all `@scope/name`, which is illegal there (`@` and `/`), so
143
+ * `@ryu/advisor` becomes `ryu.advisor`. Periods ARE legal, which is what makes the
144
+ * mapping readable rather than a hash. The mapping is lossy by construction (two
145
+ * ids could collide after normalization), so the true id always rides in
146
+ * `extensions` and this value is never treated as an identity on our side.
147
+ */
148
+ declare function toSpecName(id: string): string;
149
+ /**
150
+ * Project a Ryu `manifest.json` onto the Agent Plugins interop pair.
151
+ *
152
+ * Takes the RAW parsed manifest (not the SDK's narrower zod type) because the
153
+ * fields that matter for export — notably `mcp_servers` — live in Core's richer
154
+ * model. Throws only when the id cannot be projected onto a spec-legal name;
155
+ * every other lossy step is reported through {@link AgentPluginExport.notes}.
156
+ */
157
+ declare function toAgentPlugin(manifest: Record<string, unknown>): AgentPluginExport;
158
+
159
+ export { AGENT_PLUGINS_SPEC_VERSION, AGENT_PLUGIN_EXTENSION_NS, AGENT_PLUGIN_MANIFEST_FILE, AGENT_PLUGIN_MCP_FILE, AGENT_PLUGIN_MCP_SCHEMA_URL, AGENT_PLUGIN_SCHEMA_URL, type AgentPluginAuthor, type AgentPluginExport, type AgentPluginJson, type AgentPluginMcpJson, type AgentPluginStdioServer, type RyuExtensionData, type RyuMcpServerExtras, isAgentPluginManifest, toAgentPlugin, toSpecName };
@@ -0,0 +1,22 @@
1
+ import {
2
+ AGENT_PLUGINS_SPEC_VERSION,
3
+ AGENT_PLUGIN_EXTENSION_NS,
4
+ AGENT_PLUGIN_MANIFEST_FILE,
5
+ AGENT_PLUGIN_MCP_FILE,
6
+ AGENT_PLUGIN_MCP_SCHEMA_URL,
7
+ AGENT_PLUGIN_SCHEMA_URL,
8
+ isAgentPluginManifest,
9
+ toAgentPlugin,
10
+ toSpecName
11
+ } from "./chunk-G6FLVEC4.js";
12
+ export {
13
+ AGENT_PLUGINS_SPEC_VERSION,
14
+ AGENT_PLUGIN_EXTENSION_NS,
15
+ AGENT_PLUGIN_MANIFEST_FILE,
16
+ AGENT_PLUGIN_MCP_FILE,
17
+ AGENT_PLUGIN_MCP_SCHEMA_URL,
18
+ AGENT_PLUGIN_SCHEMA_URL,
19
+ isAgentPluginManifest,
20
+ toAgentPlugin,
21
+ toSpecName
22
+ };
@@ -118,6 +118,20 @@ var PiExtensionContributionSchema = z.object({
118
118
  /** Optional one-liner describing what the extension adds to the agent. */
119
119
  description: z.string().optional()
120
120
  });
121
+ var OutputStyleContributionSchema = z.object({
122
+ /** Stable id for this style within the plugin (`[a-z0-9][a-z0-9._-]*`). It is
123
+ * also the persisted selection key, so it must survive a settings key and a URL
124
+ * path. */
125
+ id: z.string().min(1),
126
+ /** SOURCE form: path to the Markdown file, relative to the plugin root —
127
+ * exactly `output-styles/<name>.md`. `ryu pack` replaces this with `source`. */
128
+ file: z.string().min(1).optional(),
129
+ /** WIRE form: the file's contents verbatim, frontmatter INCLUDED. The whole file
130
+ * rather than a pre-split body plus mirrored `name`/`description` keys, so a
131
+ * plugin style and a user's own `output-styles/*.md` go through one parser and
132
+ * the frontmatter stays the single source of truth for a style's metadata. */
133
+ source: z.string().optional()
134
+ });
121
135
  var DEFAULT_WIDGET_MIME = "text/html+skybridge";
122
136
  var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
123
137
  var WidgetContributionSchema = z.object({
@@ -208,7 +222,15 @@ var ContributesSchema = z.object({
208
222
  * Typed (not a loose record) because Ryu owns this vocabulary — three fields,
209
223
  * all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
210
224
  * Claude Code's to extend. */
211
- pi_extensions: z.array(PiExtensionContributionSchema).default([])
225
+ pi_extensions: z.array(PiExtensionContributionSchema).default([]),
226
+ /** Output styles the plugin ships — Markdown files that rewrite the system
227
+ * prompt's voice. Mirrors the Rust-side `Contributes.output_styles`; without it
228
+ * the CLI's zod parse would strip the declaration, and `ryu pack` would sign a
229
+ * bundle whose styles simply do not exist. Worse than the usual case of that
230
+ * bug: the styles' `.md` files are not carried by the bundle either, so there
231
+ * would be no residue to notice — the plugin would install clean and contribute
232
+ * nothing. */
233
+ output_styles: z.array(OutputStyleContributionSchema).default([])
212
234
  });
213
235
  var SetupStepSchema = z.object({
214
236
  /** Card heading (e.g. the companion app name). */
@@ -446,6 +468,7 @@ export {
446
468
  TurnHookContributionSchema,
447
469
  HookEventContributionSchema,
448
470
  PiExtensionContributionSchema,
471
+ OutputStyleContributionSchema,
449
472
  WidgetContributionSchema,
450
473
  ToolAppConfigSchema,
451
474
  ContributesSchema,