@michaelfromyeg/loom-adapter-claude 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 +303 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -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,303 @@
|
|
|
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) 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 importPlugin(dir, manifest, namespace) {
|
|
89
|
+
const components = [];
|
|
90
|
+
const files = [];
|
|
91
|
+
for (const sk of subdirsWith(join(dir, "skills"), "SKILL.md")) {
|
|
92
|
+
components.push({ skill: `skills/${sk}` });
|
|
93
|
+
files.push(...copyTree(join(dir, "skills", sk), `skills/${sk}`, "skill"));
|
|
94
|
+
}
|
|
95
|
+
for (const f of filesWithExt(join(dir, "agents"), ".md")) {
|
|
96
|
+
components.push({ agent: `agents/${f}` });
|
|
97
|
+
files.push(artifact(`agents/${f}`, readFileSync(join(dir, "agents", f)), { kind: "agent" }));
|
|
98
|
+
}
|
|
99
|
+
for (const f of filesWithExt(join(dir, "commands"), ".md")) {
|
|
100
|
+
components.push({ command: `commands/${f}` });
|
|
101
|
+
files.push(
|
|
102
|
+
artifact(`commands/${f}`, readFileSync(join(dir, "commands", f)), { kind: "command" })
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const mcpServers = manifest.mcpServers ?? readJson(join(dir, ".mcp.json"))?.mcpServers ?? {};
|
|
106
|
+
for (const [serverName, cfg] of Object.entries(mcpServers)) {
|
|
107
|
+
components.push({ mcp: `mcp/${serverName}` });
|
|
108
|
+
files.push(
|
|
109
|
+
artifact(
|
|
110
|
+
`mcp/${serverName}/server.json`,
|
|
111
|
+
json(synthesizeServerJson(namespace, serverName, cfg)),
|
|
112
|
+
{
|
|
113
|
+
kind: "mcp"
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const author2 = manifest.author;
|
|
119
|
+
const plugin = {
|
|
120
|
+
name: String(manifest.name),
|
|
121
|
+
version: String(manifest.version ?? "0.1.0"),
|
|
122
|
+
owner: {
|
|
123
|
+
name: author2?.name ?? String(manifest.name),
|
|
124
|
+
namespace,
|
|
125
|
+
...author2?.email ? { email: author2.email } : {}
|
|
126
|
+
},
|
|
127
|
+
...manifest.description ? { description: String(manifest.description) } : {},
|
|
128
|
+
components
|
|
129
|
+
};
|
|
130
|
+
return { kind: "plugin", plugin, files };
|
|
131
|
+
}
|
|
132
|
+
function importMarketplace(manifest, namespace) {
|
|
133
|
+
const owner = manifest.owner;
|
|
134
|
+
const plugins = (manifest.plugins ?? []).map((p) => ({
|
|
135
|
+
plugin: sourceToString(p.source),
|
|
136
|
+
...p.version ? { version: String(p.version) } : {},
|
|
137
|
+
...p.category ? { category: String(p.category) } : {},
|
|
138
|
+
...Array.isArray(p.tags) ? { tags: p.tags } : {}
|
|
139
|
+
}));
|
|
140
|
+
const marketplace = {
|
|
141
|
+
name: String(manifest.name),
|
|
142
|
+
owner: {
|
|
143
|
+
name: owner?.name ?? String(manifest.name),
|
|
144
|
+
namespace,
|
|
145
|
+
...owner?.email ? { email: owner.email } : {}
|
|
146
|
+
},
|
|
147
|
+
...manifest.description ? { description: String(manifest.description) } : {},
|
|
148
|
+
plugins
|
|
149
|
+
};
|
|
150
|
+
return { kind: "marketplace", marketplace };
|
|
151
|
+
}
|
|
152
|
+
function importClaude(dir, opts) {
|
|
153
|
+
const namespace = opts?.namespace ?? "com.imported";
|
|
154
|
+
const marketplace = readJson(join(dir, ".claude-plugin", "marketplace.json"));
|
|
155
|
+
if (marketplace) return importMarketplace(marketplace, namespace);
|
|
156
|
+
const manifest = readJson(join(dir, ".claude-plugin", "plugin.json")) ?? readJson(join(dir, "plugin.json"));
|
|
157
|
+
if (manifest) return importPlugin(dir, manifest, namespace);
|
|
158
|
+
if (existsSync(join(dir, "skills"))) {
|
|
159
|
+
return importPlugin(dir, { name: "imported-plugin" }, namespace);
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/mcp.ts
|
|
165
|
+
function mcpServerName(server) {
|
|
166
|
+
const raw = server.name ?? "server";
|
|
167
|
+
const afterSlash = raw.includes("/") ? raw.slice(raw.lastIndexOf("/") + 1) : raw;
|
|
168
|
+
return afterSlash || "server";
|
|
169
|
+
}
|
|
170
|
+
var RUNNERS = { npm: "npx", pypi: "uvx" };
|
|
171
|
+
function mcpRunConfig(server) {
|
|
172
|
+
const pkg = server.packages?.[0];
|
|
173
|
+
if (pkg?.identifier) {
|
|
174
|
+
if (pkg.registryType === "oci") {
|
|
175
|
+
return { command: "docker", args: ["run", "-i", "--rm", pkg.identifier] };
|
|
176
|
+
}
|
|
177
|
+
const runner = RUNNERS[pkg.registryType ?? "npm"] ?? "npx";
|
|
178
|
+
const ident = pkg.version ? `${pkg.identifier}@${pkg.version}` : pkg.identifier;
|
|
179
|
+
const args = runner === "npx" ? ["-y", ident] : [ident];
|
|
180
|
+
return { command: runner, args };
|
|
181
|
+
}
|
|
182
|
+
const remote = server.remotes?.[0];
|
|
183
|
+
if (remote?.url) {
|
|
184
|
+
const config = { type: remote.type ?? "http", url: remote.url };
|
|
185
|
+
if (remote.headers?.length) {
|
|
186
|
+
config.headers = Object.fromEntries(remote.headers.map((h) => [h.name, h.value]));
|
|
187
|
+
}
|
|
188
|
+
return config;
|
|
189
|
+
}
|
|
190
|
+
if (server.command) {
|
|
191
|
+
const config = { command: server.command };
|
|
192
|
+
if (server.args) config.args = server.args;
|
|
193
|
+
if (server.env) config.env = server.env;
|
|
194
|
+
return config;
|
|
195
|
+
}
|
|
196
|
+
return { command: "echo", args: ["server.json declares no runnable transport"] };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/index.ts
|
|
200
|
+
var TARGET_SCHEMA = "claude-code-plugin/2.1";
|
|
201
|
+
var json2 = (o) => `${JSON.stringify(o, null, 2)}
|
|
202
|
+
`;
|
|
203
|
+
function author(name, email) {
|
|
204
|
+
return email ? { name, email } : { name };
|
|
205
|
+
}
|
|
206
|
+
function copyDir(ctx, ref, destPrefix, kind) {
|
|
207
|
+
return ctx.list(ref).map((file) => {
|
|
208
|
+
const within = file.startsWith(`${ref}/`) ? file.slice(ref.length + 1) : basename(file);
|
|
209
|
+
return artifact(`${destPrefix}/${within}`, ctx.read(file), { kind });
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function copyFileOrDir(ctx, ref, destPrefix, kind) {
|
|
213
|
+
const files = ctx.list(ref);
|
|
214
|
+
if (files.length === 0) {
|
|
215
|
+
return [artifact(`${destPrefix}${extname(ref) || ".md"}`, ctx.read(ref), { kind })];
|
|
216
|
+
}
|
|
217
|
+
return copyDir(ctx, ref, destPrefix, kind);
|
|
218
|
+
}
|
|
219
|
+
var claudeAdapter = {
|
|
220
|
+
target: "claude",
|
|
221
|
+
version: "0.1.0",
|
|
222
|
+
targetSchema: TARGET_SCHEMA,
|
|
223
|
+
detect(scope, cwd) {
|
|
224
|
+
const root = scope === "user" ? join(homedir(), ".claude") : join(cwd, ".claude");
|
|
225
|
+
return {
|
|
226
|
+
root,
|
|
227
|
+
plugins: join(root, "plugins"),
|
|
228
|
+
skills: join(root, "skills"),
|
|
229
|
+
mcp: root,
|
|
230
|
+
agents: join(root, "agents"),
|
|
231
|
+
commands: join(root, "commands"),
|
|
232
|
+
hooks: join(root, "hooks"),
|
|
233
|
+
catalog: join(root, "plugins")
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
transform(component, ctx) {
|
|
237
|
+
const ref = refOf(component);
|
|
238
|
+
const leaf = leafNameOf(component);
|
|
239
|
+
switch (kindOf(component)) {
|
|
240
|
+
case "skill":
|
|
241
|
+
return copyDir(ctx, ref, `skills/${leaf}`, "skill");
|
|
242
|
+
case "agent":
|
|
243
|
+
return copyFileOrDir(ctx, ref, `agents/${leaf}`, "agent");
|
|
244
|
+
case "command":
|
|
245
|
+
return copyFileOrDir(ctx, ref, `commands/${leaf}`, "command");
|
|
246
|
+
case "hook":
|
|
247
|
+
return copyFileOrDir(ctx, ref, `hooks/${leaf}`, "hook");
|
|
248
|
+
case "mcp":
|
|
249
|
+
return copyDir(ctx, ref, `mcp/${leaf}`, "mcp");
|
|
250
|
+
case "passthrough":
|
|
251
|
+
return [
|
|
252
|
+
artifact(`hooks/${basename(ref)}`, ctx.read(ref), { kind: "hook", executable: true })
|
|
253
|
+
];
|
|
254
|
+
default:
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
emitManifest(plugin, ctx) {
|
|
259
|
+
const manifest = {
|
|
260
|
+
name: plugin.name,
|
|
261
|
+
version: plugin.version,
|
|
262
|
+
author: author(plugin.owner.name, plugin.owner.email)
|
|
263
|
+
};
|
|
264
|
+
if (plugin.description) manifest.description = plugin.description;
|
|
265
|
+
const mcpServers = {};
|
|
266
|
+
for (const c of plugin.components) {
|
|
267
|
+
if (kindOf(c) !== "mcp") continue;
|
|
268
|
+
try {
|
|
269
|
+
const server = JSON.parse(ctx.read(`${refOf(c)}/server.json`).toString("utf8"));
|
|
270
|
+
mcpServers[mcpServerName(server)] = mcpRunConfig(server);
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (Object.keys(mcpServers).length > 0) manifest.mcpServers = mcpServers;
|
|
275
|
+
return [artifact(".claude-plugin/plugin.json", json2(manifest), { kind: "manifest" })];
|
|
276
|
+
},
|
|
277
|
+
emitCatalog(marketplace) {
|
|
278
|
+
const plugins = marketplace.entries.map((entry) => {
|
|
279
|
+
const entryPlugin = {
|
|
280
|
+
name: entry.name,
|
|
281
|
+
source: entry.source.startsWith("./") ? entry.source : `./${entry.source}`
|
|
282
|
+
};
|
|
283
|
+
if (entry.description) entryPlugin.description = entry.description;
|
|
284
|
+
if (entry.version) entryPlugin.version = entry.version;
|
|
285
|
+
if (entry.category) entryPlugin.category = entry.category;
|
|
286
|
+
if (entry.tags) entryPlugin.tags = entry.tags;
|
|
287
|
+
return entryPlugin;
|
|
288
|
+
});
|
|
289
|
+
const catalog = {
|
|
290
|
+
name: marketplace.name,
|
|
291
|
+
owner: author(marketplace.owner.name, marketplace.owner.email),
|
|
292
|
+
plugins
|
|
293
|
+
};
|
|
294
|
+
if (marketplace.description) catalog.description = marketplace.description;
|
|
295
|
+
return [artifact(".claude-plugin/marketplace.json", json2(catalog), { kind: "catalog" })];
|
|
296
|
+
},
|
|
297
|
+
importNative: importClaude
|
|
298
|
+
};
|
|
299
|
+
var index_default = claudeAdapter;
|
|
300
|
+
|
|
301
|
+
export { claudeAdapter, index_default as default };
|
|
302
|
+
//# sourceMappingURL=index.js.map
|
|
303
|
+
//# 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":";;;;;;;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;AAEA,SAAS,YAAA,CACP,GAAA,EACA,QAAA,EACA,SAAA,EACgB;AAChB,EAAA,MAAM,aAA0B,EAAC;AACjC,EAAA,MAAM,QAA4B,EAAC;AAEnC,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;AACA,EAAA,KAAA,MAAW,KAAK,YAAA,CAAa,IAAA,CAAK,KAAK,QAAQ,CAAA,EAAG,KAAK,CAAA,EAAG;AACxD,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;AACA,EAAA,KAAA,MAAW,KAAK,YAAA,CAAa,IAAA,CAAK,KAAK,UAAU,CAAA,EAAG,KAAK,CAAA,EAAG;AAC1D,IAAA,UAAA,CAAW,KAAK,EAAE,OAAA,EAAS,CAAA,SAAA,EAAY,CAAC,IAAI,CAAA;AAC5C,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,QAAA,CAAS,CAAA,SAAA,EAAY,CAAC,CAAA,CAAA,EAAI,aAAa,IAAA,CAAK,GAAA,EAAK,UAAA,EAAY,CAAC,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,WAAW;AAAA,KACvF;AAAA,EACF;AAEA,EAAA,MAAM,UAAA,GACH,QAAA,CAAS,UAAA,IACT,QAAA,CAAS,IAAA,CAAK,KAAK,WAAW,CAAC,CAAA,EAAG,UAAA,IACnC,EAAC;AACH,EAAA,KAAA,MAAW,CAAC,UAAA,EAAY,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC1D,IAAA,UAAA,CAAW,KAAK,EAAE,GAAA,EAAK,CAAA,IAAA,EAAO,UAAU,IAAI,CAAA;AAC5C,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,QAAA;AAAA,QACE,OAAO,UAAU,CAAA,YAAA,CAAA;AAAA,QACjB,IAAA,CAAK,oBAAA,CAAqB,SAAA,EAAW,UAAA,EAAY,GAAG,CAAC,CAAA;AAAA,QACrD;AAAA,UACE,IAAA,EAAM;AAAA;AACR;AACF,KACF;AAAA,EACF;AAEA,EAAA,MAAMA,UAAS,QAAA,CAAS,MAAA;AACxB,EAAA,MAAM,MAAA,GAAiB;AAAA,IACrB,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA;AAAA,IAC1B,OAAA,EAAS,MAAA,CAAO,QAAA,CAAS,OAAA,IAAW,OAAO,CAAA;AAAA,IAC3C,KAAA,EAAO;AAAA,MACL,IAAA,EAAMA,OAAAA,EAAQ,IAAA,IAAQ,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,MAC1C,SAAA;AAAA,MACA,GAAIA,SAAQ,KAAA,GAAQ,EAAE,OAAOA,OAAAA,CAAO,KAAA,KAAU;AAAC,KACjD;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,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,YAAA,CAAa,KAAa,IAAA,EAA2C;AACnF,EAAA,MAAM,SAAA,GAAY,MAAM,SAAA,IAAa,cAAA;AACrC,EAAA,MAAM,cAAc,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,gBAAA,EAAkB,kBAAkB,CAAC,CAAA;AAC5E,EAAA,IAAI,WAAA,EAAa,OAAO,iBAAA,CAAkB,WAAA,EAAa,SAAS,CAAA;AAEhE,EAAA,MAAM,QAAA,GACJ,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,gBAAA,EAAkB,aAAa,CAAC,CAAA,IAAK,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,aAAa,CAAC,CAAA;AAC3F,EAAA,IAAI,QAAA,EAAU,OAAO,YAAA,CAAa,GAAA,EAAK,UAAU,SAAS,CAAA;AAG1D,EAAA,IAAI,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAC,CAAA,EAAG;AACnC,IAAA,OAAO,aAAa,GAAA,EAAK,EAAE,IAAA,EAAM,iBAAA,IAAqB,SAAS,CAAA;AAAA,EACjE;AACA,EAAA,OAAO,IAAA;AACT;;;AC/KO,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;AACf,IAAA,MAAM,MAAA,GAA0B,EAAE,IAAA,EAAM,MAAA,CAAO,QAAQ,MAAA,EAAQ,GAAA,EAAK,OAAO,GAAA,EAAI;AAC/E,IAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC1B,MAAA,MAAA,CAAO,OAAA,GAAU,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,QAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,KAAK,CAAC,CAAC,CAAA;AAAA,IAClF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;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;;;ACnDA,IAAM,aAAA,GAAgB,wBAAA;AA4BtB,IAAMC,KAAAA,GAAO,CAAC,CAAA,KAAuB,CAAA,EAAG,KAAK,SAAA,CAAU,CAAA,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAElE,SAAS,MAAA,CAAO,MAAc,KAAA,EAA8B;AAC1D,EAAA,OAAO,QAAQ,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,EAAE,IAAA,EAAK;AAC1C;AAGA,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;AAEtB,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;AAEO,IAAM,aAAA,GAAgC;AAAA,EAC3C,MAAA,EAAQ,QAAA;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,SAAS,CAAA,GAAIA,IAAAA,CAAK,GAAA,EAAK,SAAS,CAAA;AAChF,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,OAAA,EAASA,IAAAA,CAAK,IAAA,EAAM,SAAS,CAAA;AAAA,MAC7B,MAAA,EAAQA,IAAAA,CAAK,IAAA,EAAM,QAAQ,CAAA;AAAA,MAC3B,GAAA,EAAK,IAAA;AAAA,MACL,MAAA,EAAQA,IAAAA,CAAK,IAAA,EAAM,QAAQ,CAAA;AAAA,MAC3B,QAAA,EAAUA,IAAAA,CAAK,IAAA,EAAM,UAAU,CAAA;AAAA,MAC/B,KAAA,EAAOA,IAAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAAA,MACzB,OAAA,EAASA,IAAAA,CAAK,IAAA,EAAM,SAAS;AAAA,KAC/B;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;AACH,QAAA,OAAO,QAAQ,GAAA,EAAK,GAAA,EAAK,CAAA,OAAA,EAAU,IAAI,IAAI,OAAO,CAAA;AAAA,MACpD,KAAK,OAAA;AACH,QAAA,OAAO,cAAc,GAAA,EAAK,GAAA,EAAK,CAAA,OAAA,EAAU,IAAI,IAAI,OAAO,CAAA;AAAA,MAC1D,KAAK,SAAA;AACH,QAAA,OAAO,cAAc,GAAA,EAAK,GAAA,EAAK,CAAA,SAAA,EAAY,IAAI,IAAI,SAAS,CAAA;AAAA,MAC9D,KAAK,MAAA;AACH,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;AACH,QAAA,OAAO;AAAA,UACLD,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,QAAA,GAAiC;AAAA,MACrC,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,QAAQ,MAAA,CAAO,MAAA,CAAO,MAAM,IAAA,EAAM,MAAA,CAAO,MAAM,KAAK;AAAA,KACtD;AACA,IAAA,IAAI,MAAA,CAAO,WAAA,EAAa,QAAA,CAAS,WAAA,GAAc,MAAA,CAAO,WAAA;AAEtD,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;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,UAAU,EAAE,MAAA,GAAS,CAAA,WAAY,UAAA,GAAa,UAAA;AAE9D,IAAA,OAAO,CAACA,QAAAA,CAAS,4BAAA,EAA8BD,KAAAA,CAAK,QAAQ,GAAG,EAAE,IAAA,EAAM,UAAA,EAAY,CAAC,CAAA;AAAA,EACtF,CAAA;AAAA,EAEA,YAAY,WAAA,EAAsD;AAChE,IAAA,MAAM,OAAA,GAAqC,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,KAAU;AAC5E,MAAA,MAAM,WAAA,GAAuC;AAAA,QAC3C,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,WAAA,CAAY,WAAA,GAAc,KAAA,CAAM,WAAA;AACvD,MAAA,IAAI,KAAA,CAAM,OAAA,EAAS,WAAA,CAAY,OAAA,GAAU,KAAA,CAAM,OAAA;AAC/C,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,WAAA,CAAY,QAAA,GAAW,KAAA,CAAM,QAAA;AACjD,MAAA,IAAI,KAAA,CAAM,IAAA,EAAM,WAAA,CAAY,IAAA,GAAO,KAAA,CAAM,IAAA;AACzC,MAAA,OAAO,WAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,MAAM,OAAA,GAA6B;AAAA,MACjC,MAAM,WAAA,CAAY,IAAA;AAAA,MAClB,OAAO,MAAA,CAAO,WAAA,CAAY,MAAM,IAAA,EAAM,WAAA,CAAY,MAAM,KAAK,CAAA;AAAA,MAC7D;AAAA,KACF;AACA,IAAA,IAAI,WAAA,CAAY,WAAA,EAAa,OAAA,CAAQ,WAAA,GAAc,WAAA,CAAY,WAAA;AAE/D,IAAA,OAAO,CAACC,QAAAA,CAAS,iCAAA,EAAmCD,KAAAA,CAAK,OAAO,GAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,EACzF,CAAA;AAAA,EAEA,YAAA,EAAc;AAChB;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 Claude 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 type?: string;\n url?: string;\n}\n\n/** Reconstruct an MCP-standard server.json from a harness 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\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 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 {\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 Claude plugin or marketplace dir into the Loom model. */\nexport function importClaude(dir: string, opts?: ImportOptions): ImportResult | null {\n const namespace = opts?.namespace ?? \"com.imported\";\n const marketplace = readJson(join(dir, \".claude-plugin\", \"marketplace.json\"));\n if (marketplace) return importMarketplace(marketplace, namespace);\n\n const manifest =\n readJson(join(dir, \".claude-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 Claude `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\nexport interface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n type?: 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 Claude 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 const config: McpServerConfig = { type: remote.type ?? \"http\", 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 { importClaude } from \"./import\";\nimport { type McpServerConfig, mcpRunConfig, mcpServerName } from \"./mcp\";\n\n/** Bump on any change to Claude's plugin/marketplace manifest shape (spec §5). */\nconst TARGET_SCHEMA = \"claude-code-plugin/2.1\";\n\ninterface ClaudeAuthor {\n name: string;\n email?: string;\n}\ninterface ClaudePluginManifest {\n name: string;\n description?: string;\n version?: string;\n author?: ClaudeAuthor;\n mcpServers?: Record<string, McpServerConfig>;\n}\ninterface ClaudeMarketplacePlugin {\n name: string;\n source: string;\n description?: string;\n version?: string;\n category?: string;\n tags?: string[];\n}\ninterface ClaudeMarketplace {\n name: string;\n owner: ClaudeAuthor;\n description?: string;\n plugins: ClaudeMarketplacePlugin[];\n}\n\nconst json = (o: unknown): string => `${JSON.stringify(o, null, 2)}\\n`;\n\nfunction author(name: string, email?: string): ClaudeAuthor {\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 claudeAdapter: HarnessAdapter = {\n target: \"claude\",\n version: \"0.1.0\",\n targetSchema: TARGET_SCHEMA,\n\n detect(scope: Scope, cwd: string): InstallPaths {\n const root = scope === \"user\" ? join(homedir(), \".claude\") : join(cwd, \".claude\");\n return {\n root,\n plugins: join(root, \"plugins\"),\n skills: join(root, \"skills\"),\n mcp: root,\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 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: ClaudePluginManifest = {\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(\".claude-plugin/plugin.json\", json(manifest), { kind: \"manifest\" })];\n },\n\n emitCatalog(marketplace: ResolvedMarketplace): CompiledArtifact[] {\n const plugins: ClaudeMarketplacePlugin[] = marketplace.entries.map((entry) => {\n const entryPlugin: ClaudeMarketplacePlugin = {\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: ClaudeMarketplace = {\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(\".claude-plugin/marketplace.json\", json(catalog), { kind: \"catalog\" })];\n },\n\n importNative: importClaude,\n};\n\nexport default claudeAdapter;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelfromyeg/loom-adapter-claude",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Loom adapter for Claude Code: 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
|
+
"license": "MIT",
|
|
23
|
+
"author": "Michael DeMarco",
|
|
24
|
+
"homepage": "https://github.com/michaelfromyeg/loom#readme",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/michaelfromyeg/loom.git",
|
|
28
|
+
"directory": "packages/adapter-claude"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/michaelfromyeg/loom/issues"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"loom",
|
|
35
|
+
"coding-agent",
|
|
36
|
+
"ai-agent",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"codex",
|
|
39
|
+
"cursor",
|
|
40
|
+
"copilot",
|
|
41
|
+
"opencode",
|
|
42
|
+
"mcp",
|
|
43
|
+
"plugin",
|
|
44
|
+
"skill",
|
|
45
|
+
"compiler"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup"
|
|
52
|
+
}
|
|
53
|
+
}
|