@michaelfromyeg/loom-adapter-codex 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 +444 -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,444 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join, basename, extname, relative } from 'path';
|
|
3
|
+
import { artifact, parseFrontmatter } 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) return { ...base, remotes: [{ type: cfg.type ?? "streamable-http", url: cfg.url }] };
|
|
62
|
+
if (cfg.command === "npx" && Array.isArray(cfg.args)) {
|
|
63
|
+
const ident = cfg.args.find((a) => a !== "-y" && !a.startsWith("-"));
|
|
64
|
+
if (ident) {
|
|
65
|
+
const at = ident.lastIndexOf("@");
|
|
66
|
+
const id = at > 0 ? ident.slice(0, at) : ident;
|
|
67
|
+
const version = at > 0 ? ident.slice(at + 1) : void 0;
|
|
68
|
+
return {
|
|
69
|
+
...base,
|
|
70
|
+
packages: [
|
|
71
|
+
{
|
|
72
|
+
registryType: "npm",
|
|
73
|
+
identifier: id,
|
|
74
|
+
...version ? { version } : {},
|
|
75
|
+
transport: { type: "stdio" }
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
...base,
|
|
83
|
+
...cfg.command ? { command: cfg.command } : {},
|
|
84
|
+
...cfg.args ? { args: cfg.args } : {},
|
|
85
|
+
...cfg.env ? { env: cfg.env } : {}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function parseMcpServersToml(toml) {
|
|
89
|
+
const servers = {};
|
|
90
|
+
let current = null;
|
|
91
|
+
let envOf = null;
|
|
92
|
+
const unquote = (v) => v.trim().replace(/^"(.*)"$/, "$1").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
93
|
+
const parseArray = (v) => {
|
|
94
|
+
const inner = v.trim().replace(/^\[(.*)\]$/s, "$1");
|
|
95
|
+
const matches = inner.match(/"(?:[^"\\]|\\.)*"/g) ?? [];
|
|
96
|
+
return matches.map((m) => unquote(m));
|
|
97
|
+
};
|
|
98
|
+
for (const raw of toml.split("\n")) {
|
|
99
|
+
const line = raw.trim();
|
|
100
|
+
if (!line || line.startsWith("#")) continue;
|
|
101
|
+
const envHeader = line.match(/^\[mcp_servers\.([^.\]]+)\.env\]$/);
|
|
102
|
+
if (envHeader) {
|
|
103
|
+
const name = envHeader[1];
|
|
104
|
+
servers[name] ??= {};
|
|
105
|
+
const server = servers[name];
|
|
106
|
+
server.env ??= {};
|
|
107
|
+
envOf = server;
|
|
108
|
+
current = null;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const serverHeader = line.match(/^\[mcp_servers\.([^.\]]+)\]$/);
|
|
112
|
+
if (serverHeader) {
|
|
113
|
+
const name = serverHeader[1];
|
|
114
|
+
servers[name] ??= {};
|
|
115
|
+
current = servers[name];
|
|
116
|
+
envOf = null;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (line.startsWith("[")) {
|
|
120
|
+
current = null;
|
|
121
|
+
envOf = null;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const eq = line.indexOf("=");
|
|
125
|
+
if (eq < 0) continue;
|
|
126
|
+
const key = line.slice(0, eq).trim();
|
|
127
|
+
const value = line.slice(eq + 1).trim();
|
|
128
|
+
if (envOf) {
|
|
129
|
+
envOf.env ??= {};
|
|
130
|
+
envOf.env[key] = unquote(value);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (!current) continue;
|
|
134
|
+
if (key === "command") current.command = unquote(value);
|
|
135
|
+
else if (key === "url") current.url = unquote(value);
|
|
136
|
+
else if (key === "args") current.args = parseArray(value);
|
|
137
|
+
}
|
|
138
|
+
return servers;
|
|
139
|
+
}
|
|
140
|
+
function importPlugin(dir, manifest, name, namespace) {
|
|
141
|
+
const components = [];
|
|
142
|
+
const files = [];
|
|
143
|
+
for (const sk of subdirsWith(join(dir, "skills"), "SKILL.md")) {
|
|
144
|
+
components.push({ skill: `skills/${sk}` });
|
|
145
|
+
files.push(...copyTree(join(dir, "skills", sk), `skills/${sk}`, "skill"));
|
|
146
|
+
}
|
|
147
|
+
for (const f of filesWithExt(join(dir, "agents"), ".toml")) {
|
|
148
|
+
components.push({ agent: `agents/${f}` });
|
|
149
|
+
files.push(artifact(`agents/${f}`, readFileSync(join(dir, "agents", f)), { kind: "agent" }));
|
|
150
|
+
}
|
|
151
|
+
const mcpDir = join(dir, "mcp");
|
|
152
|
+
if (existsSync(mcpDir) && statSync(mcpDir).isDirectory()) {
|
|
153
|
+
for (const leaf of subdirsWith(mcpDir, "server.json")) {
|
|
154
|
+
components.push({ mcp: `mcp/${leaf}` });
|
|
155
|
+
files.push(
|
|
156
|
+
artifact(`mcp/${leaf}/server.json`, readFileSync(join(mcpDir, leaf, "server.json")), {
|
|
157
|
+
kind: "mcp"
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
} else if (existsSync(join(dir, "config.toml"))) {
|
|
162
|
+
const servers = parseMcpServersToml(readFileSync(join(dir, "config.toml"), "utf8"));
|
|
163
|
+
for (const [serverName, cfg] of Object.entries(servers)) {
|
|
164
|
+
components.push({ mcp: `mcp/${serverName}` });
|
|
165
|
+
files.push(
|
|
166
|
+
artifact(
|
|
167
|
+
`mcp/${serverName}/server.json`,
|
|
168
|
+
json(synthesizeServerJson(namespace, serverName, cfg)),
|
|
169
|
+
{ kind: "mcp" }
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const author = manifest?.author;
|
|
175
|
+
const plugin = {
|
|
176
|
+
name,
|
|
177
|
+
version: String(manifest?.version ?? "0.1.0"),
|
|
178
|
+
owner: {
|
|
179
|
+
name: author?.name ?? name,
|
|
180
|
+
namespace,
|
|
181
|
+
...author?.email ? { email: author.email } : {}
|
|
182
|
+
},
|
|
183
|
+
...manifest?.description ? { description: String(manifest.description) } : {},
|
|
184
|
+
components
|
|
185
|
+
};
|
|
186
|
+
return { kind: "plugin", plugin, files };
|
|
187
|
+
}
|
|
188
|
+
function importMarketplace(manifest, namespace) {
|
|
189
|
+
const owner = manifest.owner;
|
|
190
|
+
const plugins = (manifest.plugins ?? []).map((p) => ({
|
|
191
|
+
plugin: sourceToString(p.source),
|
|
192
|
+
...p.version ? { version: String(p.version) } : {},
|
|
193
|
+
...p.category ? { category: String(p.category) } : {},
|
|
194
|
+
...Array.isArray(p.tags) ? { tags: p.tags } : {}
|
|
195
|
+
}));
|
|
196
|
+
const marketplace = {
|
|
197
|
+
name: String(manifest.name),
|
|
198
|
+
owner: {
|
|
199
|
+
name: owner?.name ?? String(manifest.name),
|
|
200
|
+
namespace,
|
|
201
|
+
...owner?.email ? { email: owner.email } : {}
|
|
202
|
+
},
|
|
203
|
+
...manifest.description ? { description: String(manifest.description) } : {},
|
|
204
|
+
plugins
|
|
205
|
+
};
|
|
206
|
+
return { kind: "marketplace", marketplace };
|
|
207
|
+
}
|
|
208
|
+
function importCodex(dir, opts) {
|
|
209
|
+
const namespace = opts?.namespace ?? "com.imported";
|
|
210
|
+
const marketplace = readJson(join(dir, "loom-marketplace.json"));
|
|
211
|
+
if (marketplace) return importMarketplace(marketplace, namespace);
|
|
212
|
+
const manifest = readJson(join(dir, "plugin.json"));
|
|
213
|
+
const basename2 = dir.replace(/\/+$/, "").split("/").pop() || "imported-plugin";
|
|
214
|
+
const name = typeof manifest?.name === "string" ? manifest.name : basename2;
|
|
215
|
+
if (manifest || existsSync(join(dir, "skills")) || existsSync(join(dir, "agents"))) {
|
|
216
|
+
return importPlugin(dir, manifest, name, namespace);
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/mcp.ts
|
|
222
|
+
function mcpServerName(server) {
|
|
223
|
+
const raw = server.name ?? "server";
|
|
224
|
+
const afterSlash = raw.includes("/") ? raw.slice(raw.lastIndexOf("/") + 1) : raw;
|
|
225
|
+
return afterSlash || "server";
|
|
226
|
+
}
|
|
227
|
+
var RUNNERS = { npm: "npx", pypi: "uvx" };
|
|
228
|
+
function mcpRunConfig(server) {
|
|
229
|
+
const pkg = server.packages?.[0];
|
|
230
|
+
if (pkg?.identifier) {
|
|
231
|
+
if (pkg.registryType === "oci") {
|
|
232
|
+
return { command: "docker", args: ["run", "-i", "--rm", pkg.identifier] };
|
|
233
|
+
}
|
|
234
|
+
const runner = RUNNERS[pkg.registryType ?? "npm"] ?? "npx";
|
|
235
|
+
const ident = pkg.version ? `${pkg.identifier}@${pkg.version}` : pkg.identifier;
|
|
236
|
+
const args = runner === "npx" ? ["-y", ident] : [ident];
|
|
237
|
+
return { command: runner, args };
|
|
238
|
+
}
|
|
239
|
+
const remote = server.remotes?.[0];
|
|
240
|
+
if (remote?.url) {
|
|
241
|
+
return { url: remote.url };
|
|
242
|
+
}
|
|
243
|
+
if (server.command) {
|
|
244
|
+
const config = { command: server.command };
|
|
245
|
+
if (server.args) config.args = server.args;
|
|
246
|
+
if (server.env) config.env = server.env;
|
|
247
|
+
return config;
|
|
248
|
+
}
|
|
249
|
+
return { command: "echo", args: ["server.json declares no runnable transport"] };
|
|
250
|
+
}
|
|
251
|
+
function tomlString(value) {
|
|
252
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
253
|
+
}
|
|
254
|
+
function tomlArray(values) {
|
|
255
|
+
return `[${values.map(tomlString).join(", ")}]`;
|
|
256
|
+
}
|
|
257
|
+
function renderMcpServersToml(servers) {
|
|
258
|
+
const blocks = [];
|
|
259
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
260
|
+
const lines = [`[mcp_servers.${name}]`];
|
|
261
|
+
if (config.command !== void 0) lines.push(`command = ${tomlString(config.command)}`);
|
|
262
|
+
if (config.args !== void 0) lines.push(`args = ${tomlArray(config.args)}`);
|
|
263
|
+
if (config.url !== void 0) lines.push(`url = ${tomlString(config.url)}`);
|
|
264
|
+
if (config.env && Object.keys(config.env).length > 0) {
|
|
265
|
+
lines.push("");
|
|
266
|
+
lines.push(`[mcp_servers.${name}.env]`);
|
|
267
|
+
for (const [key, val] of Object.entries(config.env)) {
|
|
268
|
+
lines.push(`${key} = ${tomlString(val)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
blocks.push(lines.join("\n"));
|
|
272
|
+
}
|
|
273
|
+
return blocks.length > 0 ? `${blocks.join("\n\n")}
|
|
274
|
+
` : "";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/index.ts
|
|
278
|
+
var TARGET_SCHEMA = "codex-plugin/0.117";
|
|
279
|
+
var json2 = (o) => `${JSON.stringify(o, null, 2)}
|
|
280
|
+
`;
|
|
281
|
+
function copyDir(ctx, ref, destPrefix, kind) {
|
|
282
|
+
return ctx.list(ref).map((file) => {
|
|
283
|
+
const within = file.startsWith(`${ref}/`) ? file.slice(ref.length + 1) : basename(file);
|
|
284
|
+
return artifact(`${destPrefix}/${within}`, ctx.read(file), { kind });
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function copyFileOrDir(ctx, ref, destPrefix, kind) {
|
|
288
|
+
const files = ctx.list(ref);
|
|
289
|
+
if (files.length === 0) {
|
|
290
|
+
return [artifact(`${destPrefix}${extname(ref) || ".md"}`, ctx.read(ref), { kind })];
|
|
291
|
+
}
|
|
292
|
+
return copyDir(ctx, ref, destPrefix, kind);
|
|
293
|
+
}
|
|
294
|
+
function skillFrontmatter(ctx, ref) {
|
|
295
|
+
try {
|
|
296
|
+
const md = ctx.read(`${ref}/SKILL.md`).toString("utf8");
|
|
297
|
+
return parseFrontmatter(md).data;
|
|
298
|
+
} catch {
|
|
299
|
+
return {};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function skillSidecar(name, description) {
|
|
303
|
+
const yamlString = (v) => JSON.stringify(v);
|
|
304
|
+
return [
|
|
305
|
+
"interface:",
|
|
306
|
+
` display_name: ${yamlString(name)}`,
|
|
307
|
+
` short_description: ${yamlString(description)}`,
|
|
308
|
+
"policy:",
|
|
309
|
+
" allow_implicit_invocation: true",
|
|
310
|
+
"dependencies:",
|
|
311
|
+
" tools: []",
|
|
312
|
+
""
|
|
313
|
+
].join("\n");
|
|
314
|
+
}
|
|
315
|
+
var codexAdapter = {
|
|
316
|
+
target: "codex",
|
|
317
|
+
version: "0.1.0",
|
|
318
|
+
targetSchema: TARGET_SCHEMA,
|
|
319
|
+
detect(scope, cwd) {
|
|
320
|
+
const root = scope === "user" ? join(homedir(), ".codex") : join(cwd, ".codex");
|
|
321
|
+
const skills = scope === "user" ? join(homedir(), ".agents", "skills") : join(cwd, ".agents", "skills");
|
|
322
|
+
return {
|
|
323
|
+
root,
|
|
324
|
+
plugins: join(root, "plugins"),
|
|
325
|
+
skills,
|
|
326
|
+
// config.toml (MCP servers) lives at the `.codex` root.
|
|
327
|
+
mcp: root,
|
|
328
|
+
agents: join(root, "agents"),
|
|
329
|
+
// TODO(verify): Codex documents no dedicated commands dir; best-effort under root.
|
|
330
|
+
commands: join(root, "commands"),
|
|
331
|
+
// TODO(verify): Codex documents no hooks dir; best-effort under root.
|
|
332
|
+
hooks: join(root, "hooks"),
|
|
333
|
+
catalog: root
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
transform(component, ctx) {
|
|
337
|
+
const ref = refOf(component);
|
|
338
|
+
const leaf = leafNameOf(component);
|
|
339
|
+
switch (kindOf(component)) {
|
|
340
|
+
case "skill": {
|
|
341
|
+
const files = copyDir(ctx, ref, `skills/${leaf}`, "skill");
|
|
342
|
+
const fm = skillFrontmatter(ctx, ref);
|
|
343
|
+
const displayName = typeof fm.name === "string" ? fm.name : leaf;
|
|
344
|
+
const shortDescription = typeof fm.description === "string" ? fm.description : "";
|
|
345
|
+
files.push(
|
|
346
|
+
artifact(
|
|
347
|
+
`skills/${leaf}/agents/openai.yaml`,
|
|
348
|
+
skillSidecar(displayName, shortDescription),
|
|
349
|
+
{
|
|
350
|
+
kind: "skill"
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
return files;
|
|
355
|
+
}
|
|
356
|
+
case "agent": {
|
|
357
|
+
const md = ctx.read(ref).toString("utf8");
|
|
358
|
+
const { data, body } = parseFrontmatter(md);
|
|
359
|
+
const name = typeof data.name === "string" ? data.name : leaf;
|
|
360
|
+
const description = typeof data.description === "string" ? data.description : "";
|
|
361
|
+
return [
|
|
362
|
+
artifact(`agents/${leaf}.toml`, renderAgentToml(name, description, body), {
|
|
363
|
+
kind: "agent"
|
|
364
|
+
})
|
|
365
|
+
];
|
|
366
|
+
}
|
|
367
|
+
case "command":
|
|
368
|
+
return copyFileOrDir(ctx, ref, `commands/${leaf}`, "command");
|
|
369
|
+
case "hook":
|
|
370
|
+
return copyFileOrDir(ctx, ref, `hooks/${leaf}`, "hook");
|
|
371
|
+
case "mcp":
|
|
372
|
+
return copyDir(ctx, ref, `mcp/${leaf}`, "mcp");
|
|
373
|
+
case "passthrough":
|
|
374
|
+
return [
|
|
375
|
+
artifact(`hooks/${basename(ref)}`, ctx.read(ref), { kind: "hook", executable: true })
|
|
376
|
+
];
|
|
377
|
+
default:
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
emitManifest(plugin, ctx) {
|
|
382
|
+
const artifacts = [];
|
|
383
|
+
const mcpServers = {};
|
|
384
|
+
for (const c of plugin.components) {
|
|
385
|
+
if (kindOf(c) !== "mcp") continue;
|
|
386
|
+
try {
|
|
387
|
+
const server = JSON.parse(ctx.read(`${refOf(c)}/server.json`).toString("utf8"));
|
|
388
|
+
mcpServers[mcpServerName(server)] = mcpRunConfig(server);
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const toml = renderMcpServersToml(mcpServers);
|
|
393
|
+
if (toml) artifacts.push(artifact("config.toml", toml, { kind: "manifest" }));
|
|
394
|
+
const manifest = {
|
|
395
|
+
name: plugin.name,
|
|
396
|
+
version: plugin.version
|
|
397
|
+
};
|
|
398
|
+
if (plugin.description) manifest.description = plugin.description;
|
|
399
|
+
artifacts.push(artifact("plugin.json", json2(manifest), { kind: "manifest" }));
|
|
400
|
+
return artifacts;
|
|
401
|
+
},
|
|
402
|
+
emitCatalog(marketplace) {
|
|
403
|
+
const plugins = marketplace.entries.map((entry) => {
|
|
404
|
+
const e = {
|
|
405
|
+
name: entry.name,
|
|
406
|
+
source: entry.source.startsWith("./") ? entry.source : `./${entry.source}`
|
|
407
|
+
};
|
|
408
|
+
if (entry.description) e.description = entry.description;
|
|
409
|
+
if (entry.version) e.version = entry.version;
|
|
410
|
+
if (entry.category) e.category = entry.category;
|
|
411
|
+
if (entry.tags) e.tags = entry.tags;
|
|
412
|
+
return e;
|
|
413
|
+
});
|
|
414
|
+
const catalog = {
|
|
415
|
+
name: marketplace.name,
|
|
416
|
+
owner: marketplace.owner.email ? { name: marketplace.owner.name, email: marketplace.owner.email } : { name: marketplace.owner.name },
|
|
417
|
+
plugins
|
|
418
|
+
};
|
|
419
|
+
if (marketplace.description) catalog.description = marketplace.description;
|
|
420
|
+
return [artifact("loom-marketplace.json", json2(catalog), { kind: "catalog" })];
|
|
421
|
+
},
|
|
422
|
+
importNative: importCodex
|
|
423
|
+
};
|
|
424
|
+
function tomlString2(value) {
|
|
425
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
426
|
+
}
|
|
427
|
+
function renderAgentToml(name, description, instructions) {
|
|
428
|
+
const lines = [`name = ${tomlString2(name)}`];
|
|
429
|
+
if (description) lines.push(`description = ${tomlString2(description)}`);
|
|
430
|
+
const body = instructions.trim();
|
|
431
|
+
if (body) {
|
|
432
|
+
const escaped = body.replace(/\\/g, "\\\\").replace(/"""/g, '\\"\\"\\"');
|
|
433
|
+
lines.push(`developer_instructions = """
|
|
434
|
+
${escaped}
|
|
435
|
+
"""`);
|
|
436
|
+
}
|
|
437
|
+
return `${lines.join("\n")}
|
|
438
|
+
`;
|
|
439
|
+
}
|
|
440
|
+
var index_default = codexAdapter;
|
|
441
|
+
|
|
442
|
+
export { codexAdapter, index_default as default };
|
|
443
|
+
//# sourceMappingURL=index.js.map
|
|
444
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/import.ts","../src/mcp.ts","../src/index.ts"],"names":["basename","json","artifact","join","tomlString"],"mappings":";;;;;;;AAYA,IAAM,UAAA,GAAa,8EAAA;AACnB,IAAM,IAAA,GAAO,CAAC,CAAA,KAAuB,CAAA,EAAG,KAAK,SAAA,CAAU,CAAA,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAElE,SAAS,SAAS,IAAA,EAA8C;AAC9D,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,MAAM,CAAC,CAAA;AAAA,EAC9C,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGA,SAAS,WAAA,CAAY,KAAa,MAAA,EAA0B;AAC1D,EAAA,IAAI,CAAC,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,QAAA,CAAS,GAAG,CAAA,CAAE,WAAA,EAAY,EAAG,OAAO,EAAC;AAC9D,EAAA,OAAO,WAAA,CAAY,GAAG,CAAA,CACnB,MAAA,CAAO,CAAC,CAAA,KAAM,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,CAAC,CAAC,EAAE,WAAA,EAAY,IAAK,WAAW,IAAA,CAAK,GAAA,EAAK,GAAG,MAAM,CAAC,CAAC,CAAA,CACtF,IAAA,EAAK;AACV;AAEA,SAAS,YAAA,CAAa,KAAa,GAAA,EAAuB;AACxD,EAAA,IAAI,CAAC,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,QAAA,CAAS,GAAG,CAAA,CAAE,WAAA,EAAY,EAAG,OAAO,EAAC;AAC9D,EAAA,OAAO,YAAY,GAAG,CAAA,CACnB,OAAO,CAAC,CAAA,KAAM,EAAE,QAAA,CAAS,GAAG,KAAK,QAAA,CAAS,IAAA,CAAK,KAAK,CAAC,CAAC,EAAE,MAAA,EAAQ,EAChE,IAAA,EAAK;AACV;AAEA,SAAS,QAAA,CACP,MAAA,EACA,UAAA,EACA,IAAA,EACoB;AACpB,EAAA,MAAM,MAA0B,EAAC;AACjC,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc;AAC1B,IAAA,KAAA,MAAW,CAAA,IAAK,WAAA,CAAY,CAAC,CAAA,CAAE,MAAK,EAAG;AACrC,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,CAAA,EAAG,CAAC,CAAA;AACrB,MAAA,IAAI,SAAS,GAAG,CAAA,CAAE,WAAA,EAAY,OAAQ,GAAG,CAAA;AAAA;AAEvC,QAAA,GAAA,CAAI,KAAK,QAAA,CAAS,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,SAAS,MAAA,EAAQ,GAAG,CAAC,CAAA,CAAA,EAAI,aAAa,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,CAAC,CAAA;AAAA,IAC5F;AAAA,EACF,CAAA;AACA,EAAA,IAAA,CAAK,MAAM,CAAA;AACX,EAAA,OAAO,GAAA;AACT;AAGA,SAAS,eAAe,MAAA,EAAyB;AAC/C,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,MAAA;AACvC,EAAA,MAAM,CAAA,GAAI,MAAA;AACV,EAAA,QAAQ,GAAG,MAAA;AAAQ,IACjB,KAAK,QAAA;AACH,MAAA,OAAO,CAAA,OAAA,EAAU,CAAA,CAAE,IAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,CAAA,EAAI,CAAA,CAAE,GAAG,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA;AAAA,IACpD,KAAK,KAAA;AAAA,IACL,KAAK,YAAA;AACH,MAAA,OAAO,MAAA,CAAO,EAAE,GAAG,CAAA;AAAA,IACrB,KAAK,KAAA;AACH,MAAA,OAAO,CAAA,IAAA,EAAO,CAAA,CAAE,OAAO,CAAA,EAAG,CAAA,CAAE,UAAU,CAAA,CAAA,EAAI,CAAA,CAAE,OAAO,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA;AAAA,IAC5D;AACE,MAAA,OAAO,OAAO,MAAM,CAAA;AAAA;AAE1B;AAWA,SAAS,oBAAA,CAAqB,SAAA,EAAmB,IAAA,EAAc,GAAA,EAA4B;AACzF,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,OAAA,EAAS,UAAA;AAAA,IACT,IAAA,EAAM,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAAA,IAC1B,WAAA,EAAa,YAAY,IAAI,CAAA,YAAA,CAAA;AAAA,IAC7B,OAAA,EAAS;AAAA,GACX;AACA,EAAA,IAAI,IAAI,GAAA,EAAK,OAAO,EAAE,GAAG,MAAM,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,IAAI,IAAA,IAAQ,iBAAA,EAAmB,KAAK,GAAA,CAAI,GAAA,EAAK,CAAA,EAAE;AAChG,EAAA,IAAI,IAAI,OAAA,KAAY,KAAA,IAAS,MAAM,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA,EAAG;AACpD,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,KAAM,IAAA,IAAQ,CAAC,CAAA,CAAE,UAAA,CAAW,GAAG,CAAC,CAAA;AACnE,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,WAAA,CAAY,GAAG,CAAA;AAChC,MAAA,MAAM,KAAK,EAAA,GAAK,CAAA,GAAI,MAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA;AACzC,MAAA,MAAM,UAAU,EAAA,GAAK,CAAA,GAAI,MAAM,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA,GAAI,MAAA;AAC/C,MAAA,OAAO;AAAA,QACL,GAAG,IAAA;AAAA,QACH,QAAA,EAAU;AAAA,UACR;AAAA,YACE,YAAA,EAAc,KAAA;AAAA,YACd,UAAA,EAAY,EAAA;AAAA,YACZ,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,YAC7B,SAAA,EAAW,EAAE,IAAA,EAAM,OAAA;AAAQ;AAC7B;AACF,OACF;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,GAAI,IAAI,OAAA,GAAU,EAAE,SAAS,GAAA,CAAI,OAAA,KAAY,EAAC;AAAA,IAC9C,GAAI,IAAI,IAAA,GAAO,EAAE,MAAM,GAAA,CAAI,IAAA,KAAS,EAAC;AAAA,IACrC,GAAI,IAAI,GAAA,GAAM,EAAE,KAAK,GAAA,CAAI,GAAA,KAAQ;AAAC,GACpC;AACF;AAUA,SAAS,oBAAoB,IAAA,EAA4C;AACvE,EAAA,MAAM,UAAwC,EAAC;AAC/C,EAAA,IAAI,OAAA,GAA+B,IAAA;AACnC,EAAA,IAAI,KAAA,GAA6B,IAAA;AAEjC,EAAA,MAAM,UAAU,CAAC,CAAA,KACf,CAAA,CACG,IAAA,GACA,OAAA,CAAQ,UAAA,EAAY,IAAI,CAAA,CACxB,QAAQ,MAAA,EAAQ,GAAG,CAAA,CACnB,OAAA,CAAQ,SAAS,IAAI,CAAA;AAC1B,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAwB;AAC1C,IAAA,MAAM,QAAQ,CAAA,CAAE,IAAA,EAAK,CAAE,OAAA,CAAQ,eAAe,IAAI,CAAA;AAClD,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,KAAA,CAAM,oBAAoB,KAAK,EAAC;AACtD,IAAA,OAAO,QAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,EACtC,CAAA;AAEA,EAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,EAAG;AAClC,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,EAAK;AACtB,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AAEnC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,mCAAmC,CAAA;AAChE,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,IAAA,GAAO,UAAU,CAAC,CAAA;AACxB,MAAA,OAAA,CAAQ,IAAI,MAAM,EAAC;AACnB,MAAA,MAAM,MAAA,GAAS,QAAQ,IAAI,CAAA;AAC3B,MAAA,MAAA,CAAO,QAAQ,EAAC;AAChB,MAAA,KAAA,GAAQ,MAAA;AACR,MAAA,OAAA,GAAU,IAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,8BAA8B,CAAA;AAC9D,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAM,IAAA,GAAO,aAAa,CAAC,CAAA;AAC3B,MAAA,OAAA,CAAQ,IAAI,MAAM,EAAC;AACnB,MAAA,OAAA,GAAU,QAAQ,IAAI,CAAA;AACtB,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA;AAAA,IACF;AACA,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACxB,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,KAAA,GAAQ,IAAA;AACR,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,IAAI,KAAK,CAAA,EAAG;AACZ,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,EAAE,IAAA,EAAK;AACnC,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,CAAC,EAAE,IAAA,EAAK;AAEtC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,CAAM,QAAQ,EAAC;AACf,MAAA,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA,GAAI,OAAA,CAAQ,KAAK,CAAA;AAC9B,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAI,GAAA,KAAQ,SAAA,EAAW,OAAA,CAAQ,OAAA,GAAU,QAAQ,KAAK,CAAA;AAAA,SAAA,IAC7C,GAAA,KAAQ,KAAA,EAAO,OAAA,CAAQ,GAAA,GAAM,QAAQ,KAAK,CAAA;AAAA,SAAA,IAC1C,GAAA,KAAQ,MAAA,EAAQ,OAAA,CAAQ,IAAA,GAAO,WAAW,KAAK,CAAA;AAAA,EAC1D;AACA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,YAAA,CACP,GAAA,EACA,QAAA,EACA,IAAA,EACA,SAAA,EACgB;AAChB,EAAA,MAAM,aAA0B,EAAC;AACjC,EAAA,MAAM,QAA4B,EAAC;AAKnC,EAAA,KAAA,MAAW,MAAM,WAAA,CAAY,IAAA,CAAK,KAAK,QAAQ,CAAA,EAAG,UAAU,CAAA,EAAG;AAC7D,IAAA,UAAA,CAAW,KAAK,EAAE,KAAA,EAAO,CAAA,OAAA,EAAU,EAAE,IAAI,CAAA;AACzC,IAAA,KAAA,CAAM,IAAA,CAAK,GAAG,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,EAAE,CAAA,EAAG,CAAA,OAAA,EAAU,EAAE,CAAA,CAAA,EAAI,OAAO,CAAC,CAAA;AAAA,EAC1E;AAEA,EAAA,KAAA,MAAW,KAAK,YAAA,CAAa,IAAA,CAAK,KAAK,QAAQ,CAAA,EAAG,OAAO,CAAA,EAAG;AAC1D,IAAA,UAAA,CAAW,KAAK,EAAE,KAAA,EAAO,CAAA,OAAA,EAAU,CAAC,IAAI,CAAA;AACxC,IAAA,KAAA,CAAM,KAAK,QAAA,CAAS,CAAA,OAAA,EAAU,CAAC,CAAA,CAAA,EAAI,aAAa,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,CAAC,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,OAAA,EAAS,CAAC,CAAA;AAAA,EAC7F;AAIA,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,EAAK,KAAK,CAAA;AAC9B,EAAA,IAAI,WAAW,MAAM,CAAA,IAAK,SAAS,MAAM,CAAA,CAAE,aAAY,EAAG;AACxD,IAAA,KAAA,MAAW,IAAA,IAAQ,WAAA,CAAY,MAAA,EAAQ,aAAa,CAAA,EAAG;AACrD,MAAA,UAAA,CAAW,KAAK,EAAE,GAAA,EAAK,CAAA,IAAA,EAAO,IAAI,IAAI,CAAA;AACtC,MAAA,KAAA,CAAM,IAAA;AAAA,QACJ,QAAA,CAAS,CAAA,IAAA,EAAO,IAAI,CAAA,YAAA,CAAA,EAAgB,YAAA,CAAa,KAAK,MAAA,EAAQ,IAAA,EAAM,aAAa,CAAC,CAAA,EAAG;AAAA,UACnF,IAAA,EAAM;AAAA,SACP;AAAA,OACH;AAAA,IACF;AAAA,EACF,WAAW,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,aAAa,CAAC,CAAA,EAAG;AAC/C,IAAA,MAAM,OAAA,GAAU,oBAAoB,YAAA,CAAa,IAAA,CAAK,KAAK,aAAa,CAAA,EAAG,MAAM,CAAC,CAAA;AAClF,IAAA,KAAA,MAAW,CAAC,UAAA,EAAY,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AACvD,MAAA,UAAA,CAAW,KAAK,EAAE,GAAA,EAAK,CAAA,IAAA,EAAO,UAAU,IAAI,CAAA;AAC5C,MAAA,KAAA,CAAM,IAAA;AAAA,QACJ,QAAA;AAAA,UACE,OAAO,UAAU,CAAA,YAAA,CAAA;AAAA,UACjB,IAAA,CAAK,oBAAA,CAAqB,SAAA,EAAW,UAAA,EAAY,GAAG,CAAC,CAAA;AAAA,UACrD,EAAE,MAAM,KAAA;AAAM;AAChB,OACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAS,QAAA,EAAU,MAAA;AACzB,EAAA,MAAM,MAAA,GAAiB;AAAA,IACrB,IAAA;AAAA,IACA,OAAA,EAAS,MAAA,CAAO,QAAA,EAAU,OAAA,IAAW,OAAO,CAAA;AAAA,IAC5C,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,QAAQ,IAAA,IAAQ,IAAA;AAAA,MACtB,SAAA;AAAA,MACA,GAAI,QAAQ,KAAA,GAAQ,EAAE,OAAO,MAAA,CAAO,KAAA,KAAU;AAAC,KACjD;AAAA,IACA,GAAI,QAAA,EAAU,WAAA,GAAc,EAAE,WAAA,EAAa,OAAO,QAAA,CAAS,WAAW,CAAA,EAAE,GAAI,EAAC;AAAA,IAC7E;AAAA,GACF;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,MAAA,EAAQ,KAAA,EAAM;AACzC;AAEA,SAAS,iBAAA,CACP,UACA,SAAA,EACqB;AACrB,EAAA,MAAM,QAAQ,QAAA,CAAS,KAAA;AACvB,EAAA,MAAM,WAAY,QAAA,CAAS,OAAA,IAA8C,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACvF,MAAA,EAAQ,cAAA,CAAe,CAAA,CAAE,MAAM,CAAA;AAAA,IAC/B,GAAI,CAAA,CAAE,OAAA,GAAU,EAAE,OAAA,EAAS,OAAO,CAAA,CAAE,OAAO,CAAA,EAAE,GAAI,EAAC;AAAA,IAClD,GAAI,CAAA,CAAE,QAAA,GAAW,EAAE,QAAA,EAAU,OAAO,CAAA,CAAE,QAAQ,CAAA,EAAE,GAAI,EAAC;AAAA,IACrD,GAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,IAAI,CAAA,GAAI,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA,EAAiB,GAAI;AAAC,GAC9D,CAAE,CAAA;AACF,EAAA,MAAM,WAAA,GAA2B;AAAA,IAC/B,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA;AAAA,IAC1B,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,KAAA,EAAO,IAAA,IAAQ,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,MACzC,SAAA;AAAA,MACA,GAAI,OAAO,KAAA,GAAQ,EAAE,OAAO,KAAA,CAAM,KAAA,KAAU;AAAC,KAC/C;AAAA,IACA,GAAI,QAAA,CAAS,WAAA,GAAc,EAAE,WAAA,EAAa,OAAO,QAAA,CAAS,WAAW,CAAA,EAAE,GAAI,EAAC;AAAA,IAC5E;AAAA,GACF;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,aAAA,EAAe,WAAA,EAAY;AAC5C;AAGO,SAAS,WAAA,CAAY,KAAa,IAAA,EAA2C;AAClF,EAAA,MAAM,SAAA,GAAY,MAAM,SAAA,IAAa,cAAA;AAGrC,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,uBAAuB,CAAC,CAAA;AAC/D,EAAA,IAAI,WAAA,EAAa,OAAO,iBAAA,CAAkB,WAAA,EAAa,SAAS,CAAA;AAEhE,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,aAAa,CAAC,CAAA;AAClD,EAAA,MAAMA,SAAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,MAAA,EAAQ,EAAE,EAAE,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,EAAI,IAAK,iBAAA;AAC7D,EAAA,MAAM,OAAO,OAAO,QAAA,EAAU,IAAA,KAAS,QAAA,GAAW,SAAS,IAAA,GAAOA,SAAAA;AAGlE,EAAA,IAAI,QAAA,IAAY,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAC,CAAA,IAAK,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAC,CAAA,EAAG;AAClF,IAAA,OAAO,YAAA,CAAa,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,SAAS,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,IAAA;AACT;;;AC9PO,SAAS,cAAc,MAAA,EAA4B;AACxD,EAAA,MAAM,GAAA,GAAM,OAAO,IAAA,IAAQ,QAAA;AAC3B,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA,GAAI,CAAC,CAAA,GAAI,GAAA;AAC7E,EAAA,OAAO,UAAA,IAAc,QAAA;AACvB;AAEA,IAAM,OAAA,GAAkC,EAAE,GAAA,EAAK,KAAA,EAAO,MAAM,KAAA,EAAM;AAE3D,SAAS,aAAa,MAAA,EAAqC;AAChE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAA,GAAW,CAAC,CAAA;AAC/B,EAAA,IAAI,KAAK,UAAA,EAAY;AACnB,IAAA,IAAI,GAAA,CAAI,iBAAiB,KAAA,EAAO;AAC9B,MAAA,OAAO,EAAE,OAAA,EAAS,QAAA,EAAU,IAAA,EAAM,CAAC,OAAO,IAAA,EAAM,MAAA,EAAQ,GAAA,CAAI,UAAU,CAAA,EAAE;AAAA,IAC1E;AACA,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,YAAA,IAAgB,KAAK,CAAA,IAAK,KAAA;AACrD,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,CAAA,EAAG,GAAA,CAAI,UAAU,CAAA,CAAA,EAAI,GAAA,CAAI,OAAO,CAAA,CAAA,GAAK,GAAA,CAAI,UAAA;AACrE,IAAA,MAAM,IAAA,GAAO,WAAW,KAAA,GAAQ,CAAC,MAAM,KAAK,CAAA,GAAI,CAAC,KAAK,CAAA;AACtD,IAAA,OAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,IAAA,EAAK;AAAA,EACjC;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,GAAU,CAAC,CAAA;AACjC,EAAA,IAAI,QAAQ,GAAA,EAAK;AAGf,IAAA,OAAO,EAAE,GAAA,EAAK,MAAA,CAAO,GAAA,EAAI;AAAA,EAC3B;AAEA,EAAA,IAAI,OAAO,OAAA,EAAS;AAClB,IAAA,MAAM,MAAA,GAA0B,EAAE,OAAA,EAAS,MAAA,CAAO,OAAA,EAAQ;AAC1D,IAAA,IAAI,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,IAAA,GAAO,MAAA,CAAO,IAAA;AACtC,IAAA,IAAI,MAAA,CAAO,GAAA,EAAK,MAAA,CAAO,GAAA,GAAM,MAAA,CAAO,GAAA;AACpC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,IAAA,EAAM,CAAC,4CAA4C,CAAA,EAAE;AACjF;AAEA,SAAS,WAAW,KAAA,EAAuB;AACzC,EAAA,OAAO,CAAA,CAAA,EAAI,MAAM,OAAA,CAAQ,KAAA,EAAO,MAAM,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAC,CAAA,CAAA,CAAA;AAC9D;AAEA,SAAS,UAAU,MAAA,EAA0B;AAC3C,EAAA,OAAO,IAAI,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AAC9C;AAOO,SAAS,qBAAqB,OAAA,EAAkD;AACrF,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AACpD,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,CAAG,CAAA;AACtC,IAAA,IAAI,MAAA,CAAO,OAAA,KAAY,MAAA,EAAW,KAAA,CAAM,IAAA,CAAK,aAAa,UAAA,CAAW,MAAA,CAAO,OAAO,CAAC,CAAA,CAAE,CAAA;AACtF,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,MAAA,EAAW,KAAA,CAAM,IAAA,CAAK,UAAU,SAAA,CAAU,MAAA,CAAO,IAAI,CAAC,CAAA,CAAE,CAAA;AAC5E,IAAA,IAAI,MAAA,CAAO,GAAA,KAAQ,MAAA,EAAW,KAAA,CAAM,IAAA,CAAK,SAAS,UAAA,CAAW,MAAA,CAAO,GAAG,CAAC,CAAA,CAAE,CAAA;AAC1E,IAAA,IAAI,MAAA,CAAO,OAAO,MAAA,CAAO,IAAA,CAAK,OAAO,GAAG,CAAA,CAAE,SAAS,CAAA,EAAG;AACpD,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,IAAI,CAAA,KAAA,CAAO,CAAA;AACtC,MAAA,KAAA,MAAW,CAAC,KAAK,GAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,GAAG,CAAA,EAAG;AACnD,QAAA,KAAA,CAAM,KAAK,CAAA,EAAG,GAAG,MAAM,UAAA,CAAW,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,MAC1C;AAAA,IACF;AACA,IAAA,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EAC9B;AACA,EAAA,OAAO,OAAO,MAAA,GAAS,CAAA,GAAI,GAAG,MAAA,CAAO,IAAA,CAAK,MAAM,CAAC;AAAA,CAAA,GAAO,EAAA;AAC1D;;;AClFA,IAAM,aAAA,GAAgB,oBAAA;AAEtB,IAAMC,KAAAA,GAAO,CAAC,CAAA,KAAuB,CAAA,EAAG,KAAK,SAAA,CAAU,CAAA,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAGlE,SAAS,OAAA,CACP,GAAA,EACA,GAAA,EACA,UAAA,EACA,IAAA,EACoB;AACpB,EAAA,OAAO,IAAI,IAAA,CAAK,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,KAAS;AACjC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,UAAA,CAAW,CAAA,EAAG,GAAG,CAAA,CAAA,CAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA,GAAI,SAAS,IAAI,CAAA;AACtF,IAAA,OAAOC,QAAAA,CAAS,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG,EAAE,IAAA,EAAM,CAAA;AAAA,EACrE,CAAC,CAAA;AACH;AAGA,SAAS,aAAA,CACP,GAAA,EACA,GAAA,EACA,UAAA,EACA,IAAA,EACoB;AACpB,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,IAAA,CAAK,GAAG,CAAA;AAC1B,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,CAACA,QAAAA,CAAS,CAAA,EAAG,UAAU,CAAA,EAAG,QAAQ,GAAG,CAAA,IAAK,KAAK,CAAA,CAAA,EAAI,IAAI,IAAA,CAAK,GAAG,GAAG,EAAE,IAAA,EAAM,CAAC,CAAA;AAAA,EACpF;AACA,EAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,UAAA,EAAY,IAAI,CAAA;AAC3C;AAMA,SAAS,gBAAA,CAAiB,KAAgB,GAAA,EAAsC;AAC9E,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAK,IAAI,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,SAAA,CAAW,CAAA,CAAE,SAAS,MAAM,CAAA;AACtD,IAAA,OAAO,gBAAA,CAAiB,EAAE,CAAA,CAAE,IAAA;AAAA,EAC9B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAOA,SAAS,YAAA,CAAa,MAAc,WAAA,EAA6B;AAC/D,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAAsB,IAAA,CAAK,UAAU,CAAC,CAAA;AAC1D,EAAA,OAAO;AAAA,IACL,YAAA;AAAA,IACA,CAAA,gBAAA,EAAmB,UAAA,CAAW,IAAI,CAAC,CAAA,CAAA;AAAA,IACnC,CAAA,qBAAA,EAAwB,UAAA,CAAW,WAAW,CAAC,CAAA,CAAA;AAAA,IAC/C,SAAA;AAAA,IACA,mCAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAEO,IAAM,YAAA,GAA+B;AAAA,EAC1C,MAAA,EAAQ,OAAA;AAAA,EACR,OAAA,EAAS,OAAA;AAAA,EACT,YAAA,EAAc,aAAA;AAAA,EAEd,MAAA,CAAO,OAAc,GAAA,EAA2B;AAC9C,IAAA,MAAM,IAAA,GAAO,KAAA,KAAU,MAAA,GAASC,IAAAA,CAAK,OAAA,IAAW,QAAQ,CAAA,GAAIA,IAAAA,CAAK,GAAA,EAAK,QAAQ,CAAA;AAG9E,IAAA,MAAM,MAAA,GACJ,KAAA,KAAU,MAAA,GAASA,IAAAA,CAAK,OAAA,EAAQ,EAAG,SAAA,EAAW,QAAQ,CAAA,GAAIA,IAAAA,CAAK,GAAA,EAAK,SAAA,EAAW,QAAQ,CAAA;AACzF,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,OAAA,EAASA,IAAAA,CAAK,IAAA,EAAM,SAAS,CAAA;AAAA,MAC7B,MAAA;AAAA;AAAA,MAEA,GAAA,EAAK,IAAA;AAAA,MACL,MAAA,EAAQA,IAAAA,CAAK,IAAA,EAAM,QAAQ,CAAA;AAAA;AAAA,MAE3B,QAAA,EAAUA,IAAAA,CAAK,IAAA,EAAM,UAAU,CAAA;AAAA;AAAA,MAE/B,KAAA,EAAOA,IAAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAAA,MACzB,OAAA,EAAS;AAAA,KACX;AAAA,EACF,CAAA;AAAA,EAEA,SAAA,CAAU,WAAsB,GAAA,EAAoC;AAClE,IAAA,MAAM,GAAA,GAAM,MAAM,SAAS,CAAA;AAC3B,IAAA,MAAM,IAAA,GAAO,WAAW,SAAS,CAAA;AACjC,IAAA,QAAQ,MAAA,CAAO,SAAS,CAAA;AAAG,MACzB,KAAK,OAAA,EAAS;AACZ,QAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,EAAK,KAAK,CAAA,OAAA,EAAU,IAAI,IAAI,OAAO,CAAA;AACzD,QAAA,MAAM,EAAA,GAAK,gBAAA,CAAiB,GAAA,EAAK,GAAG,CAAA;AACpC,QAAA,MAAM,cAAc,OAAO,EAAA,CAAG,IAAA,KAAS,QAAA,GAAW,GAAG,IAAA,GAAO,IAAA;AAC5D,QAAA,MAAM,mBAAmB,OAAO,EAAA,CAAG,WAAA,KAAgB,QAAA,GAAW,GAAG,WAAA,GAAc,EAAA;AAC/E,QAAA,KAAA,CAAM,IAAA;AAAA,UACJD,QAAAA;AAAA,YACE,UAAU,IAAI,CAAA,mBAAA,CAAA;AAAA,YACd,YAAA,CAAa,aAAa,gBAAgB,CAAA;AAAA,YAC1C;AAAA,cACE,IAAA,EAAM;AAAA;AACR;AACF,SACF;AACA,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,MACA,KAAK,OAAA,EAAS;AAIZ,QAAA,MAAM,KAAK,GAAA,CAAI,IAAA,CAAK,GAAG,CAAA,CAAE,SAAS,MAAM,CAAA;AACxC,QAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,iBAAiB,EAAE,CAAA;AAC1C,QAAA,MAAM,OAAO,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,GAAW,KAAK,IAAA,GAAO,IAAA;AACzD,QAAA,MAAM,cAAc,OAAO,IAAA,CAAK,WAAA,KAAgB,QAAA,GAAW,KAAK,WAAA,GAAc,EAAA;AAC9E,QAAA,OAAO;AAAA,UACLA,QAAAA,CAAS,UAAU,IAAI,CAAA,KAAA,CAAA,EAAS,gBAAgB,IAAA,EAAM,WAAA,EAAa,IAAI,CAAA,EAAG;AAAA,YACxE,IAAA,EAAM;AAAA,WACP;AAAA,SACH;AAAA,MACF;AAAA,MACA,KAAK,SAAA;AAEH,QAAA,OAAO,cAAc,GAAA,EAAK,GAAA,EAAK,CAAA,SAAA,EAAY,IAAI,IAAI,SAAS,CAAA;AAAA,MAC9D,KAAK,MAAA;AAEH,QAAA,OAAO,cAAc,GAAA,EAAK,GAAA,EAAK,CAAA,MAAA,EAAS,IAAI,IAAI,MAAM,CAAA;AAAA,MACxD,KAAK,KAAA;AAEH,QAAA,OAAO,QAAQ,GAAA,EAAK,GAAA,EAAK,CAAA,IAAA,EAAO,IAAI,IAAI,KAAK,CAAA;AAAA,MAC/C,KAAK,aAAA;AAEH,QAAA,OAAO;AAAA,UACLA,QAAAA,CAAS,CAAA,MAAA,EAAS,QAAA,CAAS,GAAG,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,GAAG,GAAG,EAAE,IAAA,EAAM,MAAA,EAAQ,UAAA,EAAY,MAAM;AAAA,SACtF;AAAA,MACF;AACE,QAAA,OAAO,EAAC;AAAA;AACZ,EACF,CAAA;AAAA,EAEA,YAAA,CAAa,QAAgB,GAAA,EAAoC;AAC/D,IAAA,MAAM,YAAgC,EAAC;AAEvC,IAAA,MAAM,aAA8C,EAAC;AACrD,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,UAAA,EAAY;AACjC,MAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,KAAA,EAAO;AACzB,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,CAAA,EAAG,KAAA,CAAM,CAAC,CAAC,CAAA,YAAA,CAAc,CAAA,CAAE,QAAA,CAAS,MAAM,CAAC,CAAA;AAC9E,QAAA,UAAA,CAAW,aAAA,CAAc,MAAM,CAAC,CAAA,GAAI,aAAa,MAAM,CAAA;AAAA,MACzD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,qBAAqB,UAAU,CAAA;AAC5C,IAAA,IAAI,IAAA,EAAM,SAAA,CAAU,IAAA,CAAKA,QAAAA,CAAS,aAAA,EAAe,MAAM,EAAE,IAAA,EAAM,UAAA,EAAY,CAAC,CAAA;AAI5E,IAAA,MAAM,QAAA,GAAqE;AAAA,MACzE,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,SAAS,MAAA,CAAO;AAAA,KAClB;AACA,IAAA,IAAI,MAAA,CAAO,WAAA,EAAa,QAAA,CAAS,WAAA,GAAc,MAAA,CAAO,WAAA;AACtD,IAAA,SAAA,CAAU,IAAA,CAAKA,QAAAA,CAAS,aAAA,EAAeD,KAAAA,CAAK,QAAQ,GAAG,EAAE,IAAA,EAAM,UAAA,EAAY,CAAC,CAAA;AAE5E,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAAA,EAEA,YAAY,WAAA,EAAsD;AAGhE,IAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,KAAU;AACjD,MAAA,MAAM,CAAA,GAOF;AAAA,QACF,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,MAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,UAAA,CAAW,IAAI,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAA,EAAK,KAAA,CAAM,MAAM,CAAA;AAAA,OAC1E;AACA,MAAA,IAAI,KAAA,CAAM,WAAA,EAAa,CAAA,CAAE,WAAA,GAAc,KAAA,CAAM,WAAA;AAC7C,MAAA,IAAI,KAAA,CAAM,OAAA,EAAS,CAAA,CAAE,OAAA,GAAU,KAAA,CAAM,OAAA;AACrC,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,CAAA,CAAE,QAAA,GAAW,KAAA,CAAM,QAAA;AACvC,MAAA,IAAI,KAAA,CAAM,IAAA,EAAM,CAAA,CAAE,IAAA,GAAO,KAAA,CAAM,IAAA;AAC/B,MAAA,OAAO,CAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,MAAM,OAAA,GAKF;AAAA,MACF,MAAM,WAAA,CAAY,IAAA;AAAA,MAClB,OAAO,WAAA,CAAY,KAAA,CAAM,QACrB,EAAE,IAAA,EAAM,YAAY,KAAA,CAAM,IAAA,EAAM,KAAA,EAAO,WAAA,CAAY,MAAM,KAAA,EAAM,GAC/D,EAAE,IAAA,EAAM,WAAA,CAAY,MAAM,IAAA,EAAK;AAAA,MACnC;AAAA,KACF;AACA,IAAA,IAAI,WAAA,CAAY,WAAA,EAAa,OAAA,CAAQ,WAAA,GAAc,WAAA,CAAY,WAAA;AAE/D,IAAA,OAAO,CAACC,QAAAA,CAAS,uBAAA,EAAyBD,KAAAA,CAAK,OAAO,GAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,EAC/E,CAAA;AAAA,EAEA,YAAA,EAAc;AAChB;AAEA,SAASG,YAAW,KAAA,EAAuB;AACzC,EAAA,OAAO,CAAA,CAAA,EAAI,MAAM,OAAA,CAAQ,KAAA,EAAO,MAAM,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAC,CAAA,CAAA,CAAA;AAC9D;AAGA,SAAS,eAAA,CAAgB,IAAA,EAAc,WAAA,EAAqB,YAAA,EAA8B;AACxF,EAAA,MAAM,QAAQ,CAAC,CAAA,OAAA,EAAUA,WAAAA,CAAW,IAAI,CAAC,CAAA,CAAE,CAAA;AAC3C,EAAA,IAAI,aAAa,KAAA,CAAM,IAAA,CAAK,iBAAiBA,WAAAA,CAAW,WAAW,CAAC,CAAA,CAAE,CAAA;AACtE,EAAA,MAAM,IAAA,GAAO,aAAa,IAAA,EAAK;AAC/B,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,KAAA,EAAO,MAAM,CAAA,CAAE,OAAA,CAAQ,QAAQ,WAAW,CAAA;AACvE,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA;AAAA,EAAiC,OAAO;AAAA,GAAA,CAAO,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC;AAAA,CAAA;AAC5B;AAEA,IAAO,aAAA,GAAQ","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 Codex `loom-marketplace.json` entry `source`. */\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 type?: string;\n url?: string;\n}\n\n/** Reconstruct an MCP-standard server.json from a Codex run config (lossy but functional). */\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) return { ...base, remotes: [{ type: cfg.type ?? \"streamable-http\", url: cfg.url }] };\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\n/**\n * Parse the `[mcp_servers.<name>]` tables from a Codex `config.toml` fragment.\n * This is a deliberately small, hand-rolled parser for the subset our adapter\n * emits: `command = \"..\"`, `args = [\"..\", ..]`, `url = \"..\"`, and a nested\n * `[mcp_servers.<name>.env]` table of `KEY = \"..\"` string pairs.\n * TODO(verify): full Codex config.toml grammar (cwd, bearer_token_env_var,\n * http_headers, enabled_tools, etc.) is not parsed; verbatim mcp/ is preferred.\n */\nfunction parseMcpServersToml(toml: string): Record<string, McpServerCfg> {\n const servers: Record<string, McpServerCfg> = {};\n let current: McpServerCfg | null = null;\n let envOf: McpServerCfg | null = null;\n\n const unquote = (v: string): string =>\n v\n .trim()\n .replace(/^\"(.*)\"$/, \"$1\")\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\\\\\/g, \"\\\\\");\n const parseArray = (v: string): string[] => {\n const inner = v.trim().replace(/^\\[(.*)\\]$/s, \"$1\");\n const matches = inner.match(/\"(?:[^\"\\\\]|\\\\.)*\"/g) ?? [];\n return matches.map((m) => unquote(m));\n };\n\n for (const raw of toml.split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n const envHeader = line.match(/^\\[mcp_servers\\.([^.\\]]+)\\.env\\]$/);\n if (envHeader) {\n const name = envHeader[1];\n servers[name] ??= {};\n const server = servers[name];\n server.env ??= {};\n envOf = server;\n current = null;\n continue;\n }\n const serverHeader = line.match(/^\\[mcp_servers\\.([^.\\]]+)\\]$/);\n if (serverHeader) {\n const name = serverHeader[1];\n servers[name] ??= {};\n current = servers[name];\n envOf = null;\n continue;\n }\n if (line.startsWith(\"[\")) {\n current = null;\n envOf = null;\n continue;\n }\n\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n const key = line.slice(0, eq).trim();\n const value = line.slice(eq + 1).trim();\n\n if (envOf) {\n envOf.env ??= {};\n envOf.env[key] = unquote(value);\n continue;\n }\n if (!current) continue;\n if (key === \"command\") current.command = unquote(value);\n else if (key === \"url\") current.url = unquote(value);\n else if (key === \"args\") current.args = parseArray(value);\n }\n return servers;\n}\n\nfunction importPlugin(\n dir: string,\n manifest: Record<string, unknown> | null,\n name: string,\n namespace: string,\n): ImportedPlugin {\n const components: Component[] = [];\n const files: CompiledArtifact[] = [];\n\n // Skills: each skills/<name> dir with a SKILL.md. The per-skill\n // agents/openai.yaml sidecar inside is harness metadata, not a component; it\n // is carried along verbatim as a skill asset but never wired as an agent.\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 // Codex subagents are TOML files at agents/<file>.toml.\n for (const f of filesWithExt(join(dir, \"agents\"), \".toml\")) {\n components.push({ agent: `agents/${f}` });\n files.push(artifact(`agents/${f}`, readFileSync(join(dir, \"agents\", f)), { kind: \"agent\" }));\n }\n\n // MCP: prefer the verbatim server.json copies a Loom build leaves under mcp/.\n // Only when there is no mcp/ dir do we reconstruct from the config.toml tables.\n const mcpDir = join(dir, \"mcp\");\n if (existsSync(mcpDir) && statSync(mcpDir).isDirectory()) {\n for (const leaf of subdirsWith(mcpDir, \"server.json\")) {\n components.push({ mcp: `mcp/${leaf}` });\n files.push(\n artifact(`mcp/${leaf}/server.json`, readFileSync(join(mcpDir, leaf, \"server.json\")), {\n kind: \"mcp\",\n }),\n );\n }\n } else if (existsSync(join(dir, \"config.toml\"))) {\n const servers = parseMcpServersToml(readFileSync(join(dir, \"config.toml\"), \"utf8\"));\n for (const [serverName, cfg] of Object.entries(servers)) {\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,\n version: String(manifest?.version ?? \"0.1.0\"),\n owner: {\n name: author?.name ?? 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 Codex plugin or marketplace dir into the Loom model. */\nexport function importCodex(dir: string, opts?: ImportOptions): ImportResult | null {\n const namespace = opts?.namespace ?? \"com.imported\";\n\n // Codex has no native marketplace; our adapter emits a Loom-only index.\n const marketplace = readJson(join(dir, \"loom-marketplace.json\"));\n if (marketplace) return importMarketplace(marketplace, namespace);\n\n const manifest = readJson(join(dir, \"plugin.json\"));\n const basename = dir.replace(/\\/+$/, \"\").split(\"/\").pop() || \"imported-plugin\";\n const name = typeof manifest?.name === \"string\" ? manifest.name : basename;\n\n // A plugin is anything with the Codex component layout (manifest is best-effort).\n if (manifest || existsSync(join(dir, \"skills\")) || existsSync(join(dir, \"agents\"))) {\n return importPlugin(dir, manifest, name, namespace);\n }\n return null;\n}\n","/**\n * Derives a runnable Codex MCP server config from an MCP-standard `server.json`\n * (the registry's ServerJSON shape: packages[] / remotes[] / a bare command) and\n * renders it as a `config.toml` fragment under `[mcp_servers.<name>]`.\n *\n * Codex infers the transport from the keys present (`command` => stdio, `url` =>\n * http); there is NO `transport` key (harness-research.md, Codex MCP section).\n * Stored standards stay verbatim in the plugin; this is the tool-specific view.\n */\n\nexport interface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: 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 Codex 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 // Codex http servers carry only `url` here; headers/bearer-token live under\n // documented sub-keys we don't synthesize without provenance.\n return { url: remote.url };\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\nfunction tomlString(value: string): string {\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction tomlArray(values: string[]): string {\n return `[${values.map(tomlString).join(\", \")}]`;\n}\n\n/**\n * Render the `[mcp_servers.<name>]` tables for a set of servers as a single\n * `config.toml` fragment. `env` becomes a nested `[mcp_servers.<name>.env]`\n * table; transport is implied by `command` vs `url`.\n */\nexport function renderMcpServersToml(servers: Record<string, McpServerConfig>): string {\n const blocks: string[] = [];\n for (const [name, config] of Object.entries(servers)) {\n const lines = [`[mcp_servers.${name}]`];\n if (config.command !== undefined) lines.push(`command = ${tomlString(config.command)}`);\n if (config.args !== undefined) lines.push(`args = ${tomlArray(config.args)}`);\n if (config.url !== undefined) lines.push(`url = ${tomlString(config.url)}`);\n if (config.env && Object.keys(config.env).length > 0) {\n lines.push(\"\");\n lines.push(`[mcp_servers.${name}.env]`);\n for (const [key, val] of Object.entries(config.env)) {\n lines.push(`${key} = ${tomlString(val)}`);\n }\n }\n blocks.push(lines.join(\"\\n\"));\n }\n return blocks.length > 0 ? `${blocks.join(\"\\n\\n\")}\\n` : \"\";\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 parseFrontmatter,\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 { importCodex } from \"./import\";\nimport { type McpServerConfig, mcpRunConfig, mcpServerName, renderMcpServersToml } from \"./mcp\";\n\n/** Bump on any change to Codex's plugin/config/sidecar shape (spec §5). */\nconst TARGET_SCHEMA = \"codex-plugin/0.117\";\n\nconst json = (o: unknown): string => `${JSON.stringify(o, null, 2)}\\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 return [artifact(`${destPrefix}${extname(ref) || \".md\"}`, ctx.read(ref), { kind })];\n }\n return copyDir(ctx, ref, destPrefix, kind);\n}\n\n/**\n * Read a skill's SKILL.md frontmatter so the sidecar can mirror its identity.\n * Returns empty data when there is no SKILL.md or no frontmatter block.\n */\nfunction skillFrontmatter(ctx: PluginCtx, ref: string): Record<string, unknown> {\n try {\n const md = ctx.read(`${ref}/SKILL.md`).toString(\"utf8\");\n return parseFrontmatter(md).data;\n } catch {\n return {};\n }\n}\n\n/**\n * Build the per-skill `agents/openai.yaml` sidecar. Shape is CONFIRMED in\n * harness-research.md: `interface.{display_name,short_description}`,\n * `policy.allow_implicit_invocation`, `dependencies.tools`.\n */\nfunction skillSidecar(name: string, description: string): string {\n const yamlString = (v: string): string => JSON.stringify(v);\n return [\n \"interface:\",\n ` display_name: ${yamlString(name)}`,\n ` short_description: ${yamlString(description)}`,\n \"policy:\",\n \" allow_implicit_invocation: true\",\n \"dependencies:\",\n \" tools: []\",\n \"\",\n ].join(\"\\n\");\n}\n\nexport const codexAdapter: HarnessAdapter = {\n target: \"codex\",\n version: \"0.1.0\",\n targetSchema: TARGET_SCHEMA,\n\n detect(scope: Scope, cwd: string): InstallPaths {\n const root = scope === \"user\" ? join(homedir(), \".codex\") : join(cwd, \".codex\");\n // User and project skills live on the SHARED `.agents/skills` path, NOT under\n // the `.codex` root (harness-research.md, Codex Skills section).\n const skills =\n scope === \"user\" ? join(homedir(), \".agents\", \"skills\") : join(cwd, \".agents\", \"skills\");\n return {\n root,\n plugins: join(root, \"plugins\"),\n skills,\n // config.toml (MCP servers) lives at the `.codex` root.\n mcp: root,\n agents: join(root, \"agents\"),\n // TODO(verify): Codex documents no dedicated commands dir; best-effort under root.\n commands: join(root, \"commands\"),\n // TODO(verify): Codex documents no hooks dir; best-effort under root.\n hooks: join(root, \"hooks\"),\n catalog: root,\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 const files = copyDir(ctx, ref, `skills/${leaf}`, \"skill\");\n const fm = skillFrontmatter(ctx, ref);\n const displayName = typeof fm.name === \"string\" ? fm.name : leaf;\n const shortDescription = typeof fm.description === \"string\" ? fm.description : \"\";\n files.push(\n artifact(\n `skills/${leaf}/agents/openai.yaml`,\n skillSidecar(displayName, shortDescription),\n {\n kind: \"skill\",\n },\n ),\n );\n return files;\n }\n case \"agent\": {\n // Codex subagents are TOML files at agents/<leaf>.toml.\n // TODO(verify): exact subagent field set; documented fields are name,\n // description, developer_instructions (+ optional model/sandbox_mode/etc).\n const md = ctx.read(ref).toString(\"utf8\");\n const { data, body } = parseFrontmatter(md);\n const name = typeof data.name === \"string\" ? data.name : leaf;\n const description = typeof data.description === \"string\" ? data.description : \"\";\n return [\n artifact(`agents/${leaf}.toml`, renderAgentToml(name, description, body), {\n kind: \"agent\",\n }),\n ];\n }\n case \"command\":\n // TODO(verify): no documented Codex commands dir; placed best-effort.\n return copyFileOrDir(ctx, ref, `commands/${leaf}`, \"command\");\n case \"hook\":\n // TODO(verify): no documented Codex hooks dir; placed best-effort.\n return copyFileOrDir(ctx, ref, `hooks/${leaf}`, \"hook\");\n case \"mcp\":\n // Verbatim provenance copy; the runnable config goes into config.toml.\n return copyDir(ctx, ref, `mcp/${leaf}`, \"mcp\");\n case \"passthrough\":\n // TODO(verify): no documented Codex hooks dir; placed best-effort, disabled.\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 artifacts: CompiledArtifact[] = [];\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\n const toml = renderMcpServersToml(mcpServers);\n if (toml) artifacts.push(artifact(\"config.toml\", toml, { kind: \"manifest\" }));\n\n // Best-effort plugin descriptor. TODO(verify): the v0.117 plugin.json shape\n // is not fully documented; fields here are a sensible minimum.\n const manifest: { name: string; version?: string; description?: string } = {\n name: plugin.name,\n version: plugin.version,\n };\n if (plugin.description) manifest.description = plugin.description;\n artifacts.push(artifact(\"plugin.json\", json(manifest), { kind: \"manifest\" }));\n\n return artifacts;\n },\n\n emitCatalog(marketplace: ResolvedMarketplace): CompiledArtifact[] {\n // TODO(verify): Codex has no confirmed native marketplace catalog format;\n // this loom-marketplace.json index is best-effort.\n const plugins = marketplace.entries.map((entry) => {\n const e: {\n name: string;\n source: string;\n description?: string;\n version?: string;\n category?: string;\n tags?: string[];\n } = {\n name: entry.name,\n source: entry.source.startsWith(\"./\") ? entry.source : `./${entry.source}`,\n };\n if (entry.description) e.description = entry.description;\n if (entry.version) e.version = entry.version;\n if (entry.category) e.category = entry.category;\n if (entry.tags) e.tags = entry.tags;\n return e;\n });\n\n const catalog: {\n name: string;\n owner: { name: string; email?: string };\n description?: string;\n plugins: typeof plugins;\n } = {\n name: marketplace.name,\n owner: marketplace.owner.email\n ? { name: marketplace.owner.name, email: marketplace.owner.email }\n : { name: marketplace.owner.name },\n plugins,\n };\n if (marketplace.description) catalog.description = marketplace.description;\n\n return [artifact(\"loom-marketplace.json\", json(catalog), { kind: \"catalog\" })];\n },\n\n importNative: importCodex,\n};\n\nfunction tomlString(value: string): string {\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/** Render a Codex subagent TOML. Multi-line instructions use a basic string. */\nfunction renderAgentToml(name: string, description: string, instructions: string): string {\n const lines = [`name = ${tomlString(name)}`];\n if (description) lines.push(`description = ${tomlString(description)}`);\n const body = instructions.trim();\n if (body) {\n const escaped = body.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"\"\"/g, '\\\\\"\\\\\"\\\\\"');\n lines.push(`developer_instructions = \"\"\"\\n${escaped}\\n\"\"\"`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport default codexAdapter;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelfromyeg/loom-adapter-codex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Loom adapter for OpenAI Codex: config.toml MCP servers + skill sidecars + 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-schema": "0.1.0",
|
|
20
|
+
"@michaelfromyeg/loom-adapter-kit": "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-codex"
|
|
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
|
+
}
|