@michaelfromyeg/loom-adapter-cursor 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +320 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael DeMarco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join, basename, extname, relative } from 'path';
|
|
3
|
+
import { artifact } from '@michaelfromyeg/loom-adapter-kit';
|
|
4
|
+
import { kindOf, refOf, leafNameOf } from '@michaelfromyeg/loom-schema';
|
|
5
|
+
import { existsSync, readFileSync, statSync, readdirSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
var MCP_SCHEMA = "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json";
|
|
9
|
+
var json = (o) => `${JSON.stringify(o, null, 2)}
|
|
10
|
+
`;
|
|
11
|
+
function readJson(path) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function subdirsWith(dir, marker) {
|
|
19
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
20
|
+
return readdirSync(dir).filter((n) => statSync(join(dir, n)).isDirectory() && existsSync(join(dir, n, marker))).sort();
|
|
21
|
+
}
|
|
22
|
+
function filesWithExt(dir, ext) {
|
|
23
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
24
|
+
return readdirSync(dir).filter((n) => n.endsWith(ext) && statSync(join(dir, n)).isFile()).sort();
|
|
25
|
+
}
|
|
26
|
+
function copyTree(srcDir, destPrefix, kind) {
|
|
27
|
+
const out = [];
|
|
28
|
+
const walk = (d) => {
|
|
29
|
+
for (const n of readdirSync(d).sort()) {
|
|
30
|
+
const abs = join(d, n);
|
|
31
|
+
if (statSync(abs).isDirectory()) walk(abs);
|
|
32
|
+
else
|
|
33
|
+
out.push(artifact(`${destPrefix}/${relative(srcDir, abs)}`, readFileSync(abs), { kind }));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
walk(srcDir);
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function sourceToString(source) {
|
|
40
|
+
if (typeof source === "string") return source;
|
|
41
|
+
const s = source;
|
|
42
|
+
switch (s?.source) {
|
|
43
|
+
case "github":
|
|
44
|
+
return `github:${s.repo}${s.ref ? `#${s.ref}` : ""}`;
|
|
45
|
+
case "url":
|
|
46
|
+
case "git-subdir":
|
|
47
|
+
return String(s.url);
|
|
48
|
+
case "npm":
|
|
49
|
+
return `npm:${s.package}${s.version ? `@${s.version}` : ""}`;
|
|
50
|
+
default:
|
|
51
|
+
return String(source);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function synthesizeServerJson(namespace, name, cfg) {
|
|
55
|
+
const base = {
|
|
56
|
+
$schema: MCP_SCHEMA,
|
|
57
|
+
name: `${namespace}/${name}`,
|
|
58
|
+
description: `Imported ${name} MCP server.`,
|
|
59
|
+
version: "0.0.0"
|
|
60
|
+
};
|
|
61
|
+
if (cfg.url) {
|
|
62
|
+
const remote = { type: "streamable-http", url: cfg.url };
|
|
63
|
+
if (cfg.headers) {
|
|
64
|
+
remote.headers = Object.entries(cfg.headers).map(([nm, value]) => ({ name: nm, value }));
|
|
65
|
+
}
|
|
66
|
+
return { ...base, remotes: [remote] };
|
|
67
|
+
}
|
|
68
|
+
if (cfg.command === "npx" && Array.isArray(cfg.args)) {
|
|
69
|
+
const ident = cfg.args.find((a) => a !== "-y" && !a.startsWith("-"));
|
|
70
|
+
if (ident) {
|
|
71
|
+
const at = ident.lastIndexOf("@");
|
|
72
|
+
const id = at > 0 ? ident.slice(0, at) : ident;
|
|
73
|
+
const version = at > 0 ? ident.slice(at + 1) : void 0;
|
|
74
|
+
return {
|
|
75
|
+
...base,
|
|
76
|
+
packages: [
|
|
77
|
+
{
|
|
78
|
+
registryType: "npm",
|
|
79
|
+
identifier: id,
|
|
80
|
+
...version ? { version } : {},
|
|
81
|
+
transport: { type: "stdio" }
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
...base,
|
|
89
|
+
...cfg.command ? { command: cfg.command } : {},
|
|
90
|
+
...cfg.args ? { args: cfg.args } : {},
|
|
91
|
+
...cfg.env ? { env: cfg.env } : {}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function importPlugin(dir, manifest, namespace) {
|
|
95
|
+
const components = [];
|
|
96
|
+
const files = [];
|
|
97
|
+
for (const sk of subdirsWith(join(dir, "skills"), "SKILL.md")) {
|
|
98
|
+
components.push({ skill: `skills/${sk}` });
|
|
99
|
+
files.push(...copyTree(join(dir, "skills", sk), `skills/${sk}`, "skill"));
|
|
100
|
+
}
|
|
101
|
+
for (const f of filesWithExt(join(dir, "agents"), ".md")) {
|
|
102
|
+
components.push({ agent: `agents/${f}` });
|
|
103
|
+
files.push(artifact(`agents/${f}`, readFileSync(join(dir, "agents", f)), { kind: "agent" }));
|
|
104
|
+
}
|
|
105
|
+
for (const f of filesWithExt(join(dir, "commands"), ".md")) {
|
|
106
|
+
components.push({ command: `commands/${f}` });
|
|
107
|
+
files.push(
|
|
108
|
+
artifact(`commands/${f}`, readFileSync(join(dir, "commands", f)), { kind: "command" })
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const mcpRoot = join(dir, "mcp");
|
|
112
|
+
const verbatim = subdirsWith(mcpRoot, "server.json");
|
|
113
|
+
if (verbatim.length > 0) {
|
|
114
|
+
for (const leaf of verbatim) {
|
|
115
|
+
components.push({ mcp: `mcp/${leaf}` });
|
|
116
|
+
files.push(...copyTree(join(mcpRoot, leaf), `mcp/${leaf}`, "mcp"));
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
const mcpServers = manifest.mcpServers ?? readJson(join(dir, "mcp.json"))?.mcpServers ?? {};
|
|
120
|
+
for (const [serverName, cfg] of Object.entries(mcpServers)) {
|
|
121
|
+
components.push({ mcp: `mcp/${serverName}` });
|
|
122
|
+
files.push(
|
|
123
|
+
artifact(
|
|
124
|
+
`mcp/${serverName}/server.json`,
|
|
125
|
+
json(synthesizeServerJson(namespace, serverName, cfg)),
|
|
126
|
+
{ kind: "mcp" }
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const author2 = manifest.author;
|
|
132
|
+
const plugin = {
|
|
133
|
+
name: String(manifest.name),
|
|
134
|
+
version: String(manifest.version ?? "0.1.0"),
|
|
135
|
+
owner: {
|
|
136
|
+
name: author2?.name ?? String(manifest.name),
|
|
137
|
+
namespace,
|
|
138
|
+
...author2?.email ? { email: author2.email } : {}
|
|
139
|
+
},
|
|
140
|
+
...manifest.description ? { description: String(manifest.description) } : {},
|
|
141
|
+
components
|
|
142
|
+
};
|
|
143
|
+
return { kind: "plugin", plugin, files };
|
|
144
|
+
}
|
|
145
|
+
function importMarketplace(manifest, namespace) {
|
|
146
|
+
const owner = manifest.owner;
|
|
147
|
+
const plugins = (manifest.plugins ?? []).map((p) => ({
|
|
148
|
+
plugin: sourceToString(p.source),
|
|
149
|
+
...p.version ? { version: String(p.version) } : {},
|
|
150
|
+
...p.category ? { category: String(p.category) } : {},
|
|
151
|
+
...Array.isArray(p.tags) ? { tags: p.tags } : {}
|
|
152
|
+
}));
|
|
153
|
+
const marketplace = {
|
|
154
|
+
name: String(manifest.name),
|
|
155
|
+
owner: {
|
|
156
|
+
name: owner?.name ?? String(manifest.name),
|
|
157
|
+
namespace,
|
|
158
|
+
...owner?.email ? { email: owner.email } : {}
|
|
159
|
+
},
|
|
160
|
+
...manifest.description ? { description: String(manifest.description) } : {},
|
|
161
|
+
plugins
|
|
162
|
+
};
|
|
163
|
+
return { kind: "marketplace", marketplace };
|
|
164
|
+
}
|
|
165
|
+
function importCursor(dir, opts) {
|
|
166
|
+
const namespace = opts?.namespace ?? "com.imported";
|
|
167
|
+
const marketplace = readJson(join(dir, ".cursor-plugin", "marketplace.json"));
|
|
168
|
+
if (marketplace) return importMarketplace(marketplace, namespace);
|
|
169
|
+
const manifest = readJson(join(dir, ".cursor-plugin", "plugin.json")) ?? readJson(join(dir, "plugin.json"));
|
|
170
|
+
if (manifest) return importPlugin(dir, manifest, namespace);
|
|
171
|
+
if (existsSync(join(dir, "skills"))) {
|
|
172
|
+
return importPlugin(dir, { name: "imported-plugin" }, namespace);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/mcp.ts
|
|
178
|
+
function mcpServerName(server) {
|
|
179
|
+
const raw = server.name ?? "server";
|
|
180
|
+
const afterSlash = raw.includes("/") ? raw.slice(raw.lastIndexOf("/") + 1) : raw;
|
|
181
|
+
return afterSlash || "server";
|
|
182
|
+
}
|
|
183
|
+
var RUNNERS = { npm: "npx", pypi: "uvx" };
|
|
184
|
+
function mcpRunConfig(server) {
|
|
185
|
+
const pkg = server.packages?.[0];
|
|
186
|
+
if (pkg?.identifier) {
|
|
187
|
+
if (pkg.registryType === "oci") {
|
|
188
|
+
return { command: "docker", args: ["run", "-i", "--rm", pkg.identifier] };
|
|
189
|
+
}
|
|
190
|
+
const runner = RUNNERS[pkg.registryType ?? "npm"] ?? "npx";
|
|
191
|
+
const ident = pkg.version ? `${pkg.identifier}@${pkg.version}` : pkg.identifier;
|
|
192
|
+
const args = runner === "npx" ? ["-y", ident] : [ident];
|
|
193
|
+
return { command: runner, args };
|
|
194
|
+
}
|
|
195
|
+
const remote = server.remotes?.[0];
|
|
196
|
+
if (remote?.url) {
|
|
197
|
+
const config = { url: remote.url };
|
|
198
|
+
if (remote.headers?.length) {
|
|
199
|
+
config.headers = Object.fromEntries(remote.headers.map((h) => [h.name, h.value]));
|
|
200
|
+
}
|
|
201
|
+
return config;
|
|
202
|
+
}
|
|
203
|
+
if (server.command) {
|
|
204
|
+
const config = { command: server.command };
|
|
205
|
+
if (server.args) config.args = server.args;
|
|
206
|
+
if (server.env) config.env = server.env;
|
|
207
|
+
return config;
|
|
208
|
+
}
|
|
209
|
+
return { command: "echo", args: ["server.json declares no runnable transport"] };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/index.ts
|
|
213
|
+
var TARGET_SCHEMA = "cursor-plugin/1";
|
|
214
|
+
var json2 = (o) => `${JSON.stringify(o, null, 2)}
|
|
215
|
+
`;
|
|
216
|
+
function author(name, email) {
|
|
217
|
+
return email ? { name, email } : { name };
|
|
218
|
+
}
|
|
219
|
+
function copyDir(ctx, ref, destPrefix, kind) {
|
|
220
|
+
return ctx.list(ref).map((file) => {
|
|
221
|
+
const within = file.startsWith(`${ref}/`) ? file.slice(ref.length + 1) : basename(file);
|
|
222
|
+
return artifact(`${destPrefix}/${within}`, ctx.read(file), { kind });
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function copyFileOrDir(ctx, ref, destPrefix, kind) {
|
|
226
|
+
const files = ctx.list(ref);
|
|
227
|
+
if (files.length === 0) {
|
|
228
|
+
return [artifact(`${destPrefix}${extname(ref) || ".md"}`, ctx.read(ref), { kind })];
|
|
229
|
+
}
|
|
230
|
+
return copyDir(ctx, ref, destPrefix, kind);
|
|
231
|
+
}
|
|
232
|
+
var cursorAdapter = {
|
|
233
|
+
target: "cursor",
|
|
234
|
+
version: "0.1.0",
|
|
235
|
+
targetSchema: TARGET_SCHEMA,
|
|
236
|
+
detect(scope, cwd) {
|
|
237
|
+
const root = scope === "user" ? join(homedir(), ".cursor") : join(cwd, ".cursor");
|
|
238
|
+
return {
|
|
239
|
+
root,
|
|
240
|
+
plugins: join(root, "plugins"),
|
|
241
|
+
skills: join(root, "skills"),
|
|
242
|
+
// Cursor's mcp.json lives at the .cursor root, not in a category dir.
|
|
243
|
+
mcp: root,
|
|
244
|
+
// TODO(verify): Cursor auto-discovers agents/ commands/ hooks/hooks.json at a
|
|
245
|
+
// plugin root, but the user/project install dir names are unverified — using
|
|
246
|
+
// the Claude-style category dirs as a best-effort.
|
|
247
|
+
agents: join(root, "agents"),
|
|
248
|
+
commands: join(root, "commands"),
|
|
249
|
+
hooks: join(root, "hooks"),
|
|
250
|
+
catalog: join(root, "plugins")
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
transform(component, ctx) {
|
|
254
|
+
const ref = refOf(component);
|
|
255
|
+
const leaf = leafNameOf(component);
|
|
256
|
+
switch (kindOf(component)) {
|
|
257
|
+
case "skill":
|
|
258
|
+
return copyDir(ctx, ref, `skills/${leaf}`, "skill");
|
|
259
|
+
case "agent":
|
|
260
|
+
return copyFileOrDir(ctx, ref, `agents/${leaf}`, "agent");
|
|
261
|
+
case "command":
|
|
262
|
+
return copyFileOrDir(ctx, ref, `commands/${leaf}`, "command");
|
|
263
|
+
case "hook":
|
|
264
|
+
return copyFileOrDir(ctx, ref, `hooks/${leaf}`, "hook");
|
|
265
|
+
case "mcp":
|
|
266
|
+
return copyDir(ctx, ref, `mcp/${leaf}`, "mcp");
|
|
267
|
+
case "passthrough":
|
|
268
|
+
return [
|
|
269
|
+
artifact(`hooks/${basename(ref)}`, ctx.read(ref), { kind: "hook", executable: true })
|
|
270
|
+
];
|
|
271
|
+
default:
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
emitManifest(plugin, ctx) {
|
|
276
|
+
const manifest = {
|
|
277
|
+
name: plugin.name,
|
|
278
|
+
version: plugin.version,
|
|
279
|
+
author: author(plugin.owner.name, plugin.owner.email)
|
|
280
|
+
};
|
|
281
|
+
if (plugin.description) manifest.description = plugin.description;
|
|
282
|
+
const mcpServers = {};
|
|
283
|
+
for (const c of plugin.components) {
|
|
284
|
+
if (kindOf(c) !== "mcp") continue;
|
|
285
|
+
try {
|
|
286
|
+
const server = JSON.parse(ctx.read(`${refOf(c)}/server.json`).toString("utf8"));
|
|
287
|
+
mcpServers[mcpServerName(server)] = mcpRunConfig(server);
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (Object.keys(mcpServers).length > 0) manifest.mcpServers = mcpServers;
|
|
292
|
+
return [artifact(".cursor-plugin/plugin.json", json2(manifest), { kind: "manifest" })];
|
|
293
|
+
},
|
|
294
|
+
emitCatalog(marketplace) {
|
|
295
|
+
const plugins = marketplace.entries.map((entry) => {
|
|
296
|
+
const entryPlugin = {
|
|
297
|
+
name: entry.name,
|
|
298
|
+
source: entry.source.startsWith("./") ? entry.source : `./${entry.source}`
|
|
299
|
+
};
|
|
300
|
+
if (entry.description) entryPlugin.description = entry.description;
|
|
301
|
+
if (entry.version) entryPlugin.version = entry.version;
|
|
302
|
+
if (entry.category) entryPlugin.category = entry.category;
|
|
303
|
+
if (entry.tags) entryPlugin.tags = entry.tags;
|
|
304
|
+
return entryPlugin;
|
|
305
|
+
});
|
|
306
|
+
const catalog = {
|
|
307
|
+
name: marketplace.name,
|
|
308
|
+
owner: author(marketplace.owner.name, marketplace.owner.email),
|
|
309
|
+
plugins
|
|
310
|
+
};
|
|
311
|
+
if (marketplace.description) catalog.description = marketplace.description;
|
|
312
|
+
return [artifact(".cursor-plugin/marketplace.json", json2(catalog), { kind: "catalog" })];
|
|
313
|
+
},
|
|
314
|
+
importNative: importCursor
|
|
315
|
+
};
|
|
316
|
+
var index_default = cursorAdapter;
|
|
317
|
+
|
|
318
|
+
export { cursorAdapter, index_default as default };
|
|
319
|
+
//# sourceMappingURL=index.js.map
|
|
320
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/import.ts","../src/mcp.ts","../src/index.ts"],"names":["author","json","artifact","join"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport {\n artifact,\n type CompiledArtifact,\n type ImportedMarketplace,\n type ImportedPlugin,\n type ImportOptions,\n type ImportResult,\n} from \"@michaelfromyeg/loom-adapter-kit\";\nimport type { Component, Marketplace, Plugin } from \"@michaelfromyeg/loom-schema\";\n\nconst MCP_SCHEMA = \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\";\nconst json = (o: unknown): string => `${JSON.stringify(o, null, 2)}\\n`;\n\nfunction readJson(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n/** Subdirectories of `dir` that contain `marker`. */\nfunction subdirsWith(dir: string, marker: string): string[] {\n if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n return readdirSync(dir)\n .filter((n) => statSync(join(dir, n)).isDirectory() && existsSync(join(dir, n, marker)))\n .sort();\n}\n\nfunction filesWithExt(dir: string, ext: string): string[] {\n if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n return readdirSync(dir)\n .filter((n) => n.endsWith(ext) && statSync(join(dir, n)).isFile())\n .sort();\n}\n\nfunction copyTree(\n srcDir: string,\n destPrefix: string,\n kind: CompiledArtifact[\"kind\"],\n): CompiledArtifact[] {\n const out: CompiledArtifact[] = [];\n const walk = (d: string) => {\n for (const n of readdirSync(d).sort()) {\n const abs = join(d, n);\n if (statSync(abs).isDirectory()) walk(abs);\n else\n out.push(artifact(`${destPrefix}/${relative(srcDir, abs)}`, readFileSync(abs), { kind }));\n }\n };\n walk(srcDir);\n return out;\n}\n\n/** A Loom source string from a Cursor marketplace `source` (string or object). */\nfunction sourceToString(source: unknown): string {\n if (typeof source === \"string\") return source;\n const s = source as Record<string, unknown>;\n switch (s?.source) {\n case \"github\":\n return `github:${s.repo}${s.ref ? `#${s.ref}` : \"\"}`;\n case \"url\":\n case \"git-subdir\":\n return String(s.url);\n case \"npm\":\n return `npm:${s.package}${s.version ? `@${s.version}` : \"\"}`;\n default:\n return String(source);\n }\n}\n\ninterface McpServerCfg {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n}\n\n/**\n * Reconstruct an MCP-standard server.json from a Cursor `mcpServers` run config\n * (lossy but functional). Cursor has no `type` discriminator on a remote entry —\n * transport is inferred from `url` vs `command` — so a reconstructed remote\n * defaults to `streamable-http`.\n */\nfunction synthesizeServerJson(namespace: string, name: string, cfg: McpServerCfg): unknown {\n const base = {\n $schema: MCP_SCHEMA,\n name: `${namespace}/${name}`,\n description: `Imported ${name} MCP server.`,\n version: \"0.0.0\",\n };\n if (cfg.url) {\n const remote: Record<string, unknown> = { type: \"streamable-http\", url: cfg.url };\n if (cfg.headers) {\n remote.headers = Object.entries(cfg.headers).map(([nm, value]) => ({ name: nm, value }));\n }\n return { ...base, remotes: [remote] };\n }\n if (cfg.command === \"npx\" && Array.isArray(cfg.args)) {\n const ident = cfg.args.find((a) => a !== \"-y\" && !a.startsWith(\"-\"));\n if (ident) {\n const at = ident.lastIndexOf(\"@\");\n const id = at > 0 ? ident.slice(0, at) : ident;\n const version = at > 0 ? ident.slice(at + 1) : undefined;\n return {\n ...base,\n packages: [\n {\n registryType: \"npm\",\n identifier: id,\n ...(version ? { version } : {}),\n transport: { type: \"stdio\" },\n },\n ],\n };\n }\n }\n return {\n ...base,\n ...(cfg.command ? { command: cfg.command } : {}),\n ...(cfg.args ? { args: cfg.args } : {}),\n ...(cfg.env ? { env: cfg.env } : {}),\n };\n}\n\nfunction importPlugin(\n dir: string,\n manifest: Record<string, unknown>,\n namespace: string,\n): ImportedPlugin {\n const components: Component[] = [];\n const files: CompiledArtifact[] = [];\n\n for (const sk of subdirsWith(join(dir, \"skills\"), \"SKILL.md\")) {\n components.push({ skill: `skills/${sk}` });\n files.push(...copyTree(join(dir, \"skills\", sk), `skills/${sk}`, \"skill\"));\n }\n for (const f of filesWithExt(join(dir, \"agents\"), \".md\")) {\n components.push({ agent: `agents/${f}` });\n files.push(artifact(`agents/${f}`, readFileSync(join(dir, \"agents\", f)), { kind: \"agent\" }));\n }\n for (const f of filesWithExt(join(dir, \"commands\"), \".md\")) {\n components.push({ command: `commands/${f}` });\n files.push(\n artifact(`commands/${f}`, readFileSync(join(dir, \"commands\", f)), { kind: \"command\" }),\n );\n }\n\n // Prefer a verbatim mcp/<leaf>/server.json (a Loom-built plugin keeps it); only\n // reconstruct from the native `mcpServers` config when there is no mcp/ dir.\n const mcpRoot = join(dir, \"mcp\");\n const verbatim = subdirsWith(mcpRoot, \"server.json\");\n if (verbatim.length > 0) {\n for (const leaf of verbatim) {\n components.push({ mcp: `mcp/${leaf}` });\n files.push(...copyTree(join(mcpRoot, leaf), `mcp/${leaf}`, \"mcp\"));\n }\n } else {\n const mcpServers =\n (manifest.mcpServers as Record<string, McpServerCfg> | undefined) ??\n (readJson(join(dir, \"mcp.json\"))?.mcpServers as Record<string, McpServerCfg>) ??\n {};\n for (const [serverName, cfg] of Object.entries(mcpServers)) {\n components.push({ mcp: `mcp/${serverName}` });\n files.push(\n artifact(\n `mcp/${serverName}/server.json`,\n json(synthesizeServerJson(namespace, serverName, cfg)),\n { kind: \"mcp\" },\n ),\n );\n }\n }\n\n const author = manifest.author as { name?: string; email?: string } | undefined;\n const plugin: Plugin = {\n name: String(manifest.name),\n version: String(manifest.version ?? \"0.1.0\"),\n owner: {\n name: author?.name ?? String(manifest.name),\n namespace,\n ...(author?.email ? { email: author.email } : {}),\n },\n ...(manifest.description ? { description: String(manifest.description) } : {}),\n components,\n };\n return { kind: \"plugin\", plugin, files };\n}\n\nfunction importMarketplace(\n manifest: Record<string, unknown>,\n namespace: string,\n): ImportedMarketplace {\n const owner = manifest.owner as { name?: string; email?: string } | undefined;\n const plugins = ((manifest.plugins as Array<Record<string, unknown>>) ?? []).map((p) => ({\n plugin: sourceToString(p.source),\n ...(p.version ? { version: String(p.version) } : {}),\n ...(p.category ? { category: String(p.category) } : {}),\n ...(Array.isArray(p.tags) ? { tags: p.tags as string[] } : {}),\n }));\n const marketplace: Marketplace = {\n name: String(manifest.name),\n owner: {\n name: owner?.name ?? String(manifest.name),\n namespace,\n ...(owner?.email ? { email: owner.email } : {}),\n },\n ...(manifest.description ? { description: String(manifest.description) } : {}),\n plugins,\n };\n return { kind: \"marketplace\", marketplace };\n}\n\n/** Reverse-compile a Cursor plugin or marketplace dir into the Loom model. */\nexport function importCursor(dir: string, opts?: ImportOptions): ImportResult | null {\n const namespace = opts?.namespace ?? \"com.imported\";\n const marketplace = readJson(join(dir, \".cursor-plugin\", \"marketplace.json\"));\n if (marketplace) return importMarketplace(marketplace, namespace);\n\n const manifest =\n readJson(join(dir, \".cursor-plugin\", \"plugin.json\")) ?? readJson(join(dir, \"plugin.json\"));\n if (manifest) return importPlugin(dir, manifest, namespace);\n\n // No manifest, but a bare plugin layout (skills/ etc.) is still importable.\n if (existsSync(join(dir, \"skills\"))) {\n return importPlugin(dir, { name: \"imported-plugin\" }, namespace);\n }\n return null;\n}\n","/**\n * Derives a runnable Cursor `mcpServers` entry from an MCP-standard `server.json`\n * (the registry's ServerJSON shape: packages[] / remotes[] / a bare command).\n * Stored standards stay verbatim in the plugin; this is the tool-specific view.\n *\n * Cursor's `mcp.json` / plugin.json `mcpServers` map uses the same stdio\n * `{command,args,env}` and remote `{url,headers}` shape as Claude (research §Cursor,\n * \"stdio {command,args,env}; remote {url,headers,auth?}\"). Unlike Claude, Cursor's\n * remote entry has no `type` discriminator key — transport is inferred from\n * `command` vs `url`.\n */\n\nexport interface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n}\n\ninterface McpPackage {\n registryType?: string;\n identifier?: string;\n version?: string;\n transport?: { type?: string };\n}\ninterface McpRemote {\n type?: string;\n url?: string;\n headers?: Array<{ name: string; value: string }>;\n}\ninterface ServerJson {\n name?: string;\n packages?: McpPackage[];\n remotes?: McpRemote[];\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\n/** The short server key Cursor uses (the part after the reverse-DNS namespace). */\nexport function mcpServerName(server: ServerJson): string {\n const raw = server.name ?? \"server\";\n const afterSlash = raw.includes(\"/\") ? raw.slice(raw.lastIndexOf(\"/\") + 1) : raw;\n return afterSlash || \"server\";\n}\n\nconst RUNNERS: Record<string, string> = { npm: \"npx\", pypi: \"uvx\" };\n\nexport function mcpRunConfig(server: ServerJson): McpServerConfig {\n const pkg = server.packages?.[0];\n if (pkg?.identifier) {\n if (pkg.registryType === \"oci\") {\n return { command: \"docker\", args: [\"run\", \"-i\", \"--rm\", pkg.identifier] };\n }\n const runner = RUNNERS[pkg.registryType ?? \"npm\"] ?? \"npx\";\n const ident = pkg.version ? `${pkg.identifier}@${pkg.version}` : pkg.identifier;\n const args = runner === \"npx\" ? [\"-y\", ident] : [ident];\n return { command: runner, args };\n }\n\n const remote = server.remotes?.[0];\n if (remote?.url) {\n // TODO(verify): Cursor's remote mcpServers entry shape — research confirms\n // `{url, headers, auth?}` with no `type` discriminator, but the exact header\n // serialization (map vs array) is unverified; emitting a flat header map here.\n const config: McpServerConfig = { url: remote.url };\n if (remote.headers?.length) {\n config.headers = Object.fromEntries(remote.headers.map((h) => [h.name, h.value]));\n }\n return config;\n }\n\n if (server.command) {\n const config: McpServerConfig = { command: server.command };\n if (server.args) config.args = server.args;\n if (server.env) config.env = server.env;\n return config;\n }\n\n return { command: \"echo\", args: [\"server.json declares no runnable transport\"] };\n}\n","import { homedir } from \"node:os\";\nimport { basename, extname, join } from \"node:path\";\nimport {\n artifact,\n type CompiledArtifact,\n type HarnessAdapter,\n type InstallPaths,\n type PluginCtx,\n type ResolvedMarketplace,\n} from \"@michaelfromyeg/loom-adapter-kit\";\nimport {\n type Component,\n kindOf,\n leafNameOf,\n type Plugin,\n refOf,\n type Scope,\n} from \"@michaelfromyeg/loom-schema\";\nimport { importCursor } from \"./import\";\nimport { type McpServerConfig, mcpRunConfig, mcpServerName } from \"./mcp\";\n\n/** Bump on any change to Cursor's plugin/marketplace manifest shape (spec §5). */\nconst TARGET_SCHEMA = \"cursor-plugin/1\";\n\ninterface CursorAuthor {\n name: string;\n email?: string;\n}\ninterface CursorPluginManifest {\n name: string;\n description?: string;\n version?: string;\n author?: CursorAuthor;\n mcpServers?: Record<string, McpServerConfig>;\n}\ninterface CursorMarketplacePlugin {\n name: string;\n source: string;\n description?: string;\n version?: string;\n category?: string;\n tags?: string[];\n}\ninterface CursorMarketplace {\n name: string;\n owner: CursorAuthor;\n description?: string;\n plugins: CursorMarketplacePlugin[];\n}\n\nconst json = (o: unknown): string => `${JSON.stringify(o, null, 2)}\\n`;\n\nfunction author(name: string, email?: string): CursorAuthor {\n return email ? { name, email } : { name };\n}\n\n/** Copy every file under a plugin dir into `destPrefix/`, preserving structure. */\nfunction copyDir(\n ctx: PluginCtx,\n ref: string,\n destPrefix: string,\n kind: CompiledArtifact[\"kind\"],\n): CompiledArtifact[] {\n return ctx.list(ref).map((file) => {\n const within = file.startsWith(`${ref}/`) ? file.slice(ref.length + 1) : basename(file);\n return artifact(`${destPrefix}/${within}`, ctx.read(file), { kind });\n });\n}\n\n/** Place a component that may be a single Markdown file or a directory. */\nfunction copyFileOrDir(\n ctx: PluginCtx,\n ref: string,\n destPrefix: string,\n kind: CompiledArtifact[\"kind\"],\n): CompiledArtifact[] {\n const files = ctx.list(ref);\n if (files.length === 0) {\n // ref is a single file (e.g. agents/code-review.md).\n return [artifact(`${destPrefix}${extname(ref) || \".md\"}`, ctx.read(ref), { kind })];\n }\n return copyDir(ctx, ref, destPrefix, kind);\n}\n\nexport const cursorAdapter: HarnessAdapter = {\n target: \"cursor\",\n version: \"0.1.0\",\n targetSchema: TARGET_SCHEMA,\n\n detect(scope: Scope, cwd: string): InstallPaths {\n const root = scope === \"user\" ? join(homedir(), \".cursor\") : join(cwd, \".cursor\");\n return {\n root,\n plugins: join(root, \"plugins\"),\n skills: join(root, \"skills\"),\n // Cursor's mcp.json lives at the .cursor root, not in a category dir.\n mcp: root,\n // TODO(verify): Cursor auto-discovers agents/ commands/ hooks/hooks.json at a\n // plugin root, but the user/project install dir names are unverified — using\n // the Claude-style category dirs as a best-effort.\n agents: join(root, \"agents\"),\n commands: join(root, \"commands\"),\n hooks: join(root, \"hooks\"),\n catalog: join(root, \"plugins\"),\n };\n },\n\n transform(component: Component, ctx: PluginCtx): CompiledArtifact[] {\n const ref = refOf(component);\n const leaf = leafNameOf(component);\n switch (kindOf(component)) {\n case \"skill\":\n // Cursor reads .cursor/skills AND .claude/skills; copy the dir verbatim.\n return copyDir(ctx, ref, `skills/${leaf}`, \"skill\");\n case \"agent\":\n return copyFileOrDir(ctx, ref, `agents/${leaf}`, \"agent\");\n case \"command\":\n return copyFileOrDir(ctx, ref, `commands/${leaf}`, \"command\");\n case \"hook\":\n return copyFileOrDir(ctx, ref, `hooks/${leaf}`, \"hook\");\n case \"mcp\":\n // Verbatim provenance copy; the runnable config goes inline in plugin.json.\n return copyDir(ctx, ref, `mcp/${leaf}`, \"mcp\");\n case \"passthrough\":\n return [\n artifact(`hooks/${basename(ref)}`, ctx.read(ref), { kind: \"hook\", executable: true }),\n ];\n default:\n return [];\n }\n },\n\n emitManifest(plugin: Plugin, ctx: PluginCtx): CompiledArtifact[] {\n const manifest: CursorPluginManifest = {\n name: plugin.name,\n version: plugin.version,\n author: author(plugin.owner.name, plugin.owner.email),\n };\n if (plugin.description) manifest.description = plugin.description;\n\n const mcpServers: Record<string, McpServerConfig> = {};\n for (const c of plugin.components) {\n if (kindOf(c) !== \"mcp\") continue;\n try {\n const server = JSON.parse(ctx.read(`${refOf(c)}/server.json`).toString(\"utf8\"));\n mcpServers[mcpServerName(server)] = mcpRunConfig(server);\n } catch {\n // validate.ts already surfaced an error for an unparsable server.json.\n }\n }\n if (Object.keys(mcpServers).length > 0) manifest.mcpServers = mcpServers;\n\n return [artifact(\".cursor-plugin/plugin.json\", json(manifest), { kind: \"manifest\" })];\n },\n\n emitCatalog(marketplace: ResolvedMarketplace): CompiledArtifact[] {\n const plugins: CursorMarketplacePlugin[] = marketplace.entries.map((entry) => {\n const entryPlugin: CursorMarketplacePlugin = {\n name: entry.name,\n source: entry.source.startsWith(\"./\") ? entry.source : `./${entry.source}`,\n };\n if (entry.description) entryPlugin.description = entry.description;\n if (entry.version) entryPlugin.version = entry.version;\n if (entry.category) entryPlugin.category = entry.category;\n if (entry.tags) entryPlugin.tags = entry.tags;\n return entryPlugin;\n });\n\n const catalog: CursorMarketplace = {\n name: marketplace.name,\n owner: author(marketplace.owner.name, marketplace.owner.email),\n plugins,\n };\n if (marketplace.description) catalog.description = marketplace.description;\n\n return [artifact(\".cursor-plugin/marketplace.json\", json(catalog), { kind: \"catalog\" })];\n },\n\n importNative: importCursor,\n};\n\nexport default cursorAdapter;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelfromyeg/loom-adapter-cursor",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Loom adapter for Cursor: plugin.json + marketplace.json + component placement.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@michaelfromyeg/loom-adapter-kit": "0.1.0",
|
|
20
|
+
"@michaelfromyeg/loom-schema": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@michaelfromyeg/loom-core": "0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "Michael DeMarco",
|
|
27
|
+
"homepage": "https://github.com/michaelfromyeg/loom#readme",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/michaelfromyeg/loom.git",
|
|
31
|
+
"directory": "packages/adapter-cursor"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/michaelfromyeg/loom/issues"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"loom",
|
|
38
|
+
"coding-agent",
|
|
39
|
+
"ai-agent",
|
|
40
|
+
"claude-code",
|
|
41
|
+
"codex",
|
|
42
|
+
"cursor",
|
|
43
|
+
"copilot",
|
|
44
|
+
"opencode",
|
|
45
|
+
"mcp",
|
|
46
|
+
"plugin",
|
|
47
|
+
"skill",
|
|
48
|
+
"compiler"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup"
|
|
55
|
+
}
|
|
56
|
+
}
|