@kwonye/mcpx 0.1.104 → 0.1.106

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.
Files changed (79) hide show
  1. package/dist/adapters/codex.d.ts +2 -0
  2. package/dist/adapters/index.d.ts +2 -1
  3. package/dist/adapters/json.d.ts +26 -0
  4. package/dist/adapters/pi.d.ts +9 -0
  5. package/dist/chunk-0xt563m1.js +126 -0
  6. package/dist/{chunk-s9v1epnk.js → chunk-1mrkz29t.js} +71 -56
  7. package/dist/{chunk-31brdz5s.js → chunk-1v66g9d6.js} +4 -3
  8. package/dist/chunk-1yq3yb66.js +126 -0
  9. package/dist/chunk-27mq4agf.js +33 -0
  10. package/dist/{chunk-941dqsx2.js → chunk-4aafp444.js} +8175 -8066
  11. package/dist/chunk-4xym942z.js +36 -0
  12. package/dist/{chunk-tnhekcpv.js → chunk-56z18csx.js} +1 -1
  13. package/dist/{chunk-bnbz55g6.js → chunk-65kshh9w.js} +1 -1
  14. package/dist/{chunk-xydajvzg.js → chunk-7ky3rsw9.js} +1 -1
  15. package/dist/chunk-7rq88hb3.js +563 -0
  16. package/dist/chunk-8v13hqrp.js +825 -0
  17. package/dist/chunk-a2gen8bn.js +730 -0
  18. package/dist/{chunk-wq4zd3t7.js → chunk-brxwa30j.js} +130 -111
  19. package/dist/chunk-chaj8a8b.js +45 -0
  20. package/dist/chunk-e96643mj.js +26 -0
  21. package/dist/{chunk-02r7k4ax.js → chunk-ew06e41w.js} +12 -6
  22. package/dist/chunk-ezk8txcy.js +23 -0
  23. package/dist/{chunk-cqenqnxk.js → chunk-grkz21sq.js} +35 -3
  24. package/dist/{chunk-12vgqm89.js → chunk-gvjxpk98.js} +1 -1
  25. package/dist/chunk-h9kp47rp.js +18 -0
  26. package/dist/chunk-hahs24dv.js +127 -0
  27. package/dist/{chunk-z34m83m4.js → chunk-jv0s0cp6.js} +5 -3
  28. package/dist/{chunk-enzmq965.js → chunk-n0462d52.js} +1 -1
  29. package/dist/chunk-nh6hhv29.js +138 -0
  30. package/dist/chunk-p7yzyt87.js +19426 -0
  31. package/dist/chunk-q86c5s6r.js +7 -0
  32. package/dist/chunk-qf8bj9y7.js +254 -0
  33. package/dist/chunk-t20f49g3.js +30 -0
  34. package/dist/{chunk-b58zsdp4.js → chunk-v041118a.js} +17 -5
  35. package/dist/{chunk-91vcwk5m.js → chunk-v14bg5yp.js} +1 -1
  36. package/dist/chunk-wvzap0na.js +18166 -0
  37. package/dist/chunk-wztmp5b4.js +30479 -0
  38. package/dist/chunk-xvqr1q9b.js +14 -0
  39. package/dist/{chunk-0xhdyt4z.js → chunk-yj7mpczw.js} +17 -5
  40. package/dist/chunk-yr0txkw0.js +31 -0
  41. package/dist/cli.js +3977 -1045
  42. package/dist/core/adoption.d.ts +62 -0
  43. package/dist/core/analytics.d.ts +17 -0
  44. package/dist/core/consumers.d.ts +8 -0
  45. package/dist/core/control.d.ts +12 -0
  46. package/dist/core/daemon.d.ts +2 -2
  47. package/dist/core/file-state.d.ts +5 -0
  48. package/dist/core/harnesses.d.ts +25 -0
  49. package/dist/core/index.d.ts +17 -7
  50. package/dist/core/index.js +128 -62
  51. package/dist/core/managed-index-lock.d.ts +1 -1
  52. package/dist/core/management.d.ts +32 -0
  53. package/dist/core/native-document.d.ts +4 -0
  54. package/dist/core/operations.d.ts +52 -0
  55. package/dist/core/paths.d.ts +2 -0
  56. package/dist/core/plugin-manager.d.ts +4 -1
  57. package/dist/core/plugin-projections.d.ts +1 -0
  58. package/dist/core/runtime-entry.d.ts +6 -0
  59. package/dist/core/runtime-snapshot.d.ts +6 -0
  60. package/dist/core/secret-keychain.d.ts +31 -0
  61. package/dist/core/secrets.d.ts +12 -2
  62. package/dist/core/served-skills.d.ts +62 -0
  63. package/dist/core/share.d.ts +71 -0
  64. package/dist/core/skills.d.ts +7 -0
  65. package/dist/core/status.d.ts +1 -0
  66. package/dist/core/sync-plan.d.ts +14 -0
  67. package/dist/core/sync.d.ts +3 -0
  68. package/dist/core/telemetry.d.ts +2 -2
  69. package/dist/core/tui.d.ts +20 -0
  70. package/dist/core/verify-upstreams.d.ts +4 -0
  71. package/dist/core/work-limiter.d.ts +10 -0
  72. package/dist/gateway/server.d.ts +2 -0
  73. package/dist/types.d.ts +72 -1
  74. package/dist/util/fs.d.ts +2 -0
  75. package/dist/version.d.ts +1 -1
  76. package/package.json +1 -1
  77. package/dist/adapters/opencode.d.ts +0 -11
  78. package/dist/chunk-cz8cdmw1.js +0 -344
  79. package/dist/chunk-z1k63fa3.js +0 -3760
@@ -0,0 +1,36 @@
1
+ import {
2
+ buildDaemonChildEnv,
3
+ getDaemonStatus,
4
+ readDaemonLogs,
5
+ resolveGatewayPort,
6
+ restartDaemon,
7
+ runDaemonForeground,
8
+ startDaemon,
9
+ stopDaemon
10
+ } from "./chunk-wvzap0na.js";
11
+ import"./chunk-1mrkz29t.js";
12
+ import"./chunk-4aafp444.js";
13
+ import"./chunk-a2gen8bn.js";
14
+ import"./chunk-wztmp5b4.js";
15
+ import"./chunk-8v13hqrp.js";
16
+ import"./chunk-t20f49g3.js";
17
+ import"./chunk-hahs24dv.js";
18
+ import"./chunk-chaj8a8b.js";
19
+ import"./chunk-7rq88hb3.js";
20
+ import"./chunk-brxwa30j.js";
21
+ import"./chunk-p7yzyt87.js";
22
+ import"./chunk-grkz21sq.js";
23
+ import"./chunk-bjw71y54.js";
24
+ import"./chunk-wj4tjacg.js";
25
+ export {
26
+ buildDaemonChildEnv,
27
+ getDaemonStatus,
28
+ readDaemonLogs,
29
+ resolveGatewayPort,
30
+ restartDaemon,
31
+ runDaemonForeground,
32
+ startDaemon,
33
+ stopDaemon
34
+ };
35
+
36
+ //# debugId=7E85224A0D63747064756E2164756E21
@@ -1484,4 +1484,4 @@ var require_src = __commonJS(function(exports) {
1484
1484
 
1485
1485
  export { require_src };
1486
1486
 
1487
- //# debugId=6F7C2FCF52BCCAE864756E2164756E21
1487
+ //# debugId=695EAAE1C53734E664756E2164756E21
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-c7zg7ey3.js";
4
4
  import {
5
5
  require_src
6
- } from "./chunk-tnhekcpv.js";
6
+ } from "./chunk-56z18csx.js";
7
7
  import {
8
8
  __commonJS,
9
9
  __require
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-c7zg7ey3.js";
4
4
  import {
5
5
  require_src
6
- } from "./chunk-tnhekcpv.js";
6
+ } from "./chunk-56z18csx.js";
7
7
  import {
8
8
  __commonJS
9
9
  } from "./chunk-wj4tjacg.js";
@@ -0,0 +1,563 @@
1
+ import {
2
+ loadConfig
3
+ } from "./chunk-brxwa30j.js";
4
+ import {
5
+ exports_external
6
+ } from "./chunk-p7yzyt87.js";
7
+ import {
8
+ ensureDir,
9
+ getPluginCacheRoot,
10
+ getSkillPackagesRoot,
11
+ getSkillStatePath,
12
+ getSkillsDir
13
+ } from "./chunk-grkz21sq.js";
14
+
15
+ // src/core/skills.ts
16
+ import fs2 from "node:fs";
17
+ import path2 from "node:path";
18
+
19
+ // src/core/identifiers.ts
20
+ var skillIdSchema = exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
21
+ function validateSkillId(id) {
22
+ const result = skillIdSchema.safeParse(id);
23
+ if (!result.success) {
24
+ throw new Error(`Invalid skill id "${id}". Use letters, numbers, dots, underscores, and hyphens.`);
25
+ }
26
+ return id;
27
+ }
28
+ function normalizePluginName(name) {
29
+ const normalized = name.replace(/[\\/]/g, "_").replace(/^\.+/, "").replace(/[^a-zA-Z0-9._-]/g, "_");
30
+ if (!normalized) {
31
+ throw new Error(`Invalid plugin name "${name}"`);
32
+ }
33
+ return normalized;
34
+ }
35
+
36
+ // src/core/plugin-source.ts
37
+ var GITHUB_RE = /^(?:github\.com\/)?([a-zA-Z0-9][\w.-]*\/[a-zA-Z0-9][\w.-]*?)(?:@([\w./-]+))?$/;
38
+ var GIT_URL_RE = /^https?:\/\/.*\.git(?:\?.*)?(?:\/\/.*)?$/;
39
+ var SCP_GIT_RE = /^[\w.-]+@[\w.-]+:[\w./-]+$/;
40
+ var NPM_RE = /^npm:(@?[\w.-]+(?:\/[\w.-]+)?)(?:@([\w.-]+))?$/;
41
+ var MARKETPLACE_RE = /^marketplace:([\w.-]+)$/;
42
+ function parseSource(source) {
43
+ const github = source.match(GITHUB_RE);
44
+ if (github) {
45
+ return {
46
+ type: "github",
47
+ original: source,
48
+ ref: github[2] || undefined
49
+ };
50
+ }
51
+ if (NPM_RE.test(source)) {
52
+ const npm = source.match(NPM_RE);
53
+ return {
54
+ type: "npm",
55
+ original: source,
56
+ ref: npm[2] || undefined
57
+ };
58
+ }
59
+ if (MARKETPLACE_RE.test(source)) {
60
+ return {
61
+ type: "marketplace",
62
+ original: source
63
+ };
64
+ }
65
+ if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../") || source === "." || source === "..") {
66
+ return {
67
+ type: "local",
68
+ original: source
69
+ };
70
+ }
71
+ if (GIT_URL_RE.test(source) || SCP_GIT_RE.test(source)) {
72
+ return {
73
+ type: "git",
74
+ original: source
75
+ };
76
+ }
77
+ throw new Error(`Unrecognized plugin source: ${source}`);
78
+ }
79
+
80
+ // src/core/plugin-cache.ts
81
+ import fs from "node:fs";
82
+ import path from "node:path";
83
+ import crypto from "node:crypto";
84
+ import { execFile } from "node:child_process";
85
+ import { promisify } from "node:util";
86
+ var TMP_PREFIX = "mcpx-acquire-";
87
+ var execFileAsync = promisify(execFile);
88
+
89
+ class PluginCache {
90
+ cacheRoot;
91
+ constructor() {
92
+ this.cacheRoot = getPluginCacheRoot();
93
+ ensureDir(this.cacheRoot);
94
+ }
95
+ sourceDir(source) {
96
+ const key = source.type === "github" ? `github_${source.original.split("@")[0].replace(/[^a-zA-Z0-9_/-]/g, "_")}` : source.type === "npm" ? `npm_${source.original.replace(/^npm:/, "").split("@")[0].replace(/[^a-zA-Z0-9_/-]/g, "_")}` : source.type === "local" ? `local_${source.original.replace(/[^a-zA-Z0-9_/-]/g, "_")}` : source.type === "git-subdir" ? `git_subdir_${`${source.original}/${source.path ?? ""}`.replace(/[^a-zA-Z0-9_./@-]/g, "_")}` : `git_${source.original.replace(/[^a-zA-Z0-9_./@-]/g, "_")}`;
97
+ return path.join(this.cacheRoot, key);
98
+ }
99
+ refPath(srcDir) {
100
+ return path.join(srcDir, "ref.json");
101
+ }
102
+ shaDir(srcDir, sha) {
103
+ return path.join(srcDir, sha);
104
+ }
105
+ async resolveSha(source) {
106
+ const srcDir = this.sourceDir(source);
107
+ if (source.type === "github") {
108
+ const repo = source.original.replace(/^github\.com\//, "").split("@")[0];
109
+ const ref = source.ref || "HEAD";
110
+ const { stdout } = await execFileAsync("git", ["ls-remote", `https://github.com/${repo}.git`, ref], { timeout: 30000 });
111
+ const sha = stdout.split("\t")[0]?.trim();
112
+ if (!sha)
113
+ throw new Error(`Could not resolve ref ${ref} for ${repo}`);
114
+ return sha;
115
+ }
116
+ if (source.type === "git" || source.type === "git-subdir") {
117
+ const ref = source.ref || "HEAD";
118
+ const { stdout } = await execFileAsync("git", ["ls-remote", source.original, ref], { timeout: 30000 });
119
+ const sha = stdout.split("\t")[0]?.trim();
120
+ if (!sha)
121
+ throw new Error(`Could not resolve ref ${ref} for ${source.original}`);
122
+ return sha;
123
+ }
124
+ if (source.type === "local") {
125
+ const resolved = path.resolve(source.original);
126
+ if (!fs.existsSync(resolved)) {
127
+ throw new Error(`Local path not found: ${resolved}`);
128
+ }
129
+ return computeTreeHash(resolved);
130
+ }
131
+ if (source.type === "npm") {
132
+ const pkg = source.original.replace(/^npm:/, "");
133
+ const spec = source.ref ? `${pkg}@${source.ref}` : pkg;
134
+ const { stdout } = await execFileAsync("npm", ["view", spec, "version", "dist.integrity", "--json"], { timeout: 30000 });
135
+ const metadata = JSON.parse(stdout);
136
+ if (!metadata.version)
137
+ throw new Error(`Could not resolve npm package ${spec}`);
138
+ return `npm-${metadata.version}-${metadata.dist?.integrity ?? metadata["dist.integrity"] ?? "unverified"}`;
139
+ }
140
+ throw new Error(`Cannot resolve SHA for source type: ${source.type}`);
141
+ }
142
+ async fetch(source, pluginName) {
143
+ const sha = source.resolvedSha || await this.resolveSha(source);
144
+ const srcDir = this.sourceDir(source);
145
+ const destDir = this.shaDir(srcDir, sha);
146
+ if (fs.existsSync(destDir)) {
147
+ this.updateRef(srcDir, sha);
148
+ return { source: source.original, name: pluginName, sha, root: destDir };
149
+ }
150
+ ensureDir(srcDir);
151
+ if (source.type === "github" || source.type === "git" || source.type === "git-subdir") {
152
+ const remote = source.type === "github" ? `https://github.com/${source.original.replace(/^github\.com\//, "").split("@")[0]}.git` : source.original;
153
+ const target = source.resolvedSha || source.ref || "HEAD";
154
+ const tmpDir = fs.mkdtempSync(path.join(this.cacheRoot, TMP_PREFIX));
155
+ try {
156
+ await execFileAsync("git", ["init"], { cwd: tmpDir, timeout: 1e4 });
157
+ await execFileAsync("git", ["remote", "add", "origin", remote], { cwd: tmpDir, timeout: 1e4 });
158
+ await execFileAsync("git", ["fetch", "--depth", "1", "origin", target], {
159
+ cwd: tmpDir,
160
+ timeout: 60000
161
+ });
162
+ await execFileAsync("git", ["checkout", "--detach", "FETCH_HEAD"], { cwd: tmpDir, timeout: 1e4 });
163
+ const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], {
164
+ cwd: tmpDir,
165
+ timeout: 1e4
166
+ });
167
+ const actualSha = stdout.trim();
168
+ fs.rmSync(path.join(tmpDir, ".git"), { recursive: true, force: true });
169
+ const finalDest = this.shaDir(srcDir, actualSha);
170
+ if (fs.existsSync(finalDest)) {
171
+ fs.rmSync(tmpDir, { recursive: true, force: true });
172
+ this.updateRef(srcDir, actualSha);
173
+ return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
174
+ }
175
+ if (source.type === "git-subdir") {
176
+ if (!source.path)
177
+ throw new Error("git-subdir source is missing a path");
178
+ const subdir = path.resolve(tmpDir, source.path);
179
+ if (!subdir.startsWith(path.resolve(tmpDir) + path.sep) || !fs.existsSync(subdir)) {
180
+ throw new Error(`Plugin subdirectory not found: ${source.path}`);
181
+ }
182
+ try {
183
+ fs.renameSync(subdir, finalDest);
184
+ } catch (error) {
185
+ if (!fs.existsSync(finalDest))
186
+ throw error;
187
+ }
188
+ fs.rmSync(tmpDir, { recursive: true, force: true });
189
+ } else {
190
+ try {
191
+ fs.renameSync(tmpDir, finalDest);
192
+ } catch (error) {
193
+ if (!fs.existsSync(finalDest))
194
+ throw error;
195
+ return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
196
+ }
197
+ }
198
+ this.updateRef(srcDir, actualSha);
199
+ return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
200
+ } catch (e) {
201
+ if (fs.existsSync(tmpDir))
202
+ fs.rmSync(tmpDir, { recursive: true, force: true });
203
+ throw e;
204
+ }
205
+ }
206
+ if (source.type === "local") {
207
+ const resolved = path.resolve(source.original);
208
+ if (!fs.existsSync(resolved)) {
209
+ throw new Error(`Local path not found: ${resolved}`);
210
+ }
211
+ copyDirSync(resolved, destDir);
212
+ this.updateRef(srcDir, sha);
213
+ return { source: source.original, name: pluginName, sha, root: destDir };
214
+ }
215
+ if (source.type === "npm") {
216
+ const pkg = source.original.replace(/^npm:/, "");
217
+ const spec = source.ref ? `${pkg}@${source.ref}` : pkg;
218
+ const tmpDir = fs.mkdtempSync(path.join(this.cacheRoot, TMP_PREFIX));
219
+ try {
220
+ const { stdout } = await execFileAsync("npm", ["pack", spec, "--ignore-scripts", "--json", "--pack-destination", tmpDir], { timeout: 120000 });
221
+ const packed = JSON.parse(stdout);
222
+ const filename = packed[0]?.filename;
223
+ if (!filename)
224
+ throw new Error(`npm did not produce an archive for ${spec}`);
225
+ const archive = path.join(tmpDir, filename);
226
+ const actualSha = `npm-${packed[0]?.version ?? source.ref ?? "latest"}-${crypto.createHash("sha256").update(fs.readFileSync(archive)).digest("hex")}`;
227
+ const finalDest = this.shaDir(srcDir, actualSha);
228
+ if (!fs.existsSync(finalDest)) {
229
+ ensureDir(finalDest);
230
+ await execFileAsync("tar", ["-xzf", archive, "--strip-components=1", "-C", finalDest], { timeout: 30000 });
231
+ }
232
+ this.updateRef(srcDir, actualSha);
233
+ return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
234
+ } finally {
235
+ fs.rmSync(tmpDir, { recursive: true, force: true });
236
+ }
237
+ }
238
+ throw new Error(`Cannot fetch source type: ${source.type}`);
239
+ }
240
+ updateRef(srcDir, sha) {
241
+ const refs = {};
242
+ if (fs.existsSync(this.refPath(srcDir))) {
243
+ try {
244
+ const existing = JSON.parse(fs.readFileSync(this.refPath(srcDir), "utf8"));
245
+ Object.assign(refs, existing);
246
+ } catch {}
247
+ }
248
+ refs.latest = sha;
249
+ fs.writeFileSync(this.refPath(srcDir), JSON.stringify(refs, null, 2));
250
+ }
251
+ listCached() {
252
+ const results = [];
253
+ try {
254
+ const sourceDirs = fs.readdirSync(this.cacheRoot);
255
+ for (const dir of sourceDirs) {
256
+ const srcDir = path.join(this.cacheRoot, dir);
257
+ if (!fs.statSync(srcDir).isDirectory())
258
+ continue;
259
+ const entries = fs.readdirSync(srcDir);
260
+ for (const entry of entries) {
261
+ if (entry === "ref.json")
262
+ continue;
263
+ const shaDir = path.join(srcDir, entry);
264
+ if (!fs.statSync(shaDir).isDirectory())
265
+ continue;
266
+ const refs = {};
267
+ if (fs.existsSync(this.refPath(srcDir))) {
268
+ try {
269
+ Object.assign(refs, JSON.parse(fs.readFileSync(this.refPath(srcDir), "utf8")));
270
+ } catch {}
271
+ }
272
+ results.push({
273
+ source: dir,
274
+ name: entry,
275
+ sha: entry,
276
+ root: shaDir
277
+ });
278
+ }
279
+ }
280
+ } catch {}
281
+ return results;
282
+ }
283
+ remove(source, sha) {
284
+ const srcDir = this.sourceDir(source);
285
+ const target = this.shaDir(srcDir, sha);
286
+ if (fs.existsSync(target)) {
287
+ fs.rmSync(target, { recursive: true, force: true });
288
+ }
289
+ }
290
+ removeAll(source) {
291
+ const srcDir = this.sourceDir(source);
292
+ if (fs.existsSync(srcDir)) {
293
+ fs.rmSync(srcDir, { recursive: true, force: true });
294
+ }
295
+ }
296
+ }
297
+ function computeTreeHash(dir) {
298
+ const entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
299
+ const hash = crypto.createHash("sha256");
300
+ for (const entry of entries) {
301
+ if (entry.name.startsWith(".git"))
302
+ continue;
303
+ if (entry.name === ".DS_Store")
304
+ continue;
305
+ if (entry.isDirectory()) {
306
+ hash.update(entry.name);
307
+ hash.update(computeTreeHash(path.join(dir, entry.name)));
308
+ } else if (entry.isFile()) {
309
+ hash.update(entry.name);
310
+ hash.update(fs.readFileSync(path.join(dir, entry.name)));
311
+ }
312
+ }
313
+ return hash.digest("hex");
314
+ }
315
+ function copyDirSync(src, dest) {
316
+ ensureDir(dest);
317
+ const entries = fs.readdirSync(src, { withFileTypes: true });
318
+ for (const entry of entries) {
319
+ if (entry.name.startsWith(".git"))
320
+ continue;
321
+ if (entry.name === ".DS_Store")
322
+ continue;
323
+ const srcPath = path.join(src, entry.name);
324
+ const destPath = path.join(dest, entry.name);
325
+ if (entry.isDirectory()) {
326
+ copyDirSync(srcPath, destPath);
327
+ } else if (entry.isFile()) {
328
+ fs.copyFileSync(srcPath, destPath);
329
+ }
330
+ }
331
+ }
332
+
333
+ // src/core/skills.ts
334
+ var skillStateSchema = exports_external.record(exports_external.string(), exports_external.object({ source: exports_external.string(), skillName: exports_external.string().optional(), pinned: exports_external.boolean().default(false), previousPath: exports_external.string().optional() }));
335
+ function readSkillState() {
336
+ const statePath = getSkillStatePath();
337
+ if (!fs2.existsSync(statePath))
338
+ return {};
339
+ try {
340
+ return skillStateSchema.parse(JSON.parse(fs2.readFileSync(statePath, "utf8")));
341
+ } catch {
342
+ return {};
343
+ }
344
+ }
345
+ function writeSkillState(state) {
346
+ ensureDir(path2.dirname(getSkillStatePath()));
347
+ fs2.writeFileSync(getSkillStatePath(), JSON.stringify(state, null, 2) + `
348
+ `, "utf8");
349
+ }
350
+ function validateSkillPackage(root) {
351
+ const skillPath = path2.join(root, "SKILL.md");
352
+ if (!fs2.existsSync(skillPath))
353
+ throw new Error(`Agent Skill is missing SKILL.md: ${root}`);
354
+ const content = fs2.readFileSync(skillPath, "utf8");
355
+ const match = content.match(/^---\s*[\s\S]*?^name:\s*([^\r\n]+)[\s\S]*?^description:\s*([^\r\n]+)[\s\S]*?^---/m);
356
+ if (!match || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(match[1].trim()) || !match[2].trim()) {
357
+ throw new Error(`Invalid Agent Skill frontmatter in ${skillPath}; name and description are required`);
358
+ }
359
+ }
360
+ function installedSkills() {
361
+ const root = getSkillPackagesRoot();
362
+ if (!fs2.existsSync(root))
363
+ return [];
364
+ return fs2.readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
365
+ if (!entry.isDirectory())
366
+ return [];
367
+ const packageRoot = path2.join(root, entry.name);
368
+ const skillFile = path2.join(packageRoot, "SKILL.md");
369
+ if (!fs2.existsSync(skillFile))
370
+ return [];
371
+ return [{ id: entry.name, content: fs2.readFileSync(skillFile, "utf8"), root: packageRoot, source: "installed", readOnly: true }];
372
+ });
373
+ }
374
+ function listSkills() {
375
+ const skillsDir = getSkillsDir();
376
+ const skills = [];
377
+ const files = fs2.existsSync(skillsDir) ? fs2.readdirSync(skillsDir, { withFileTypes: true }) : [];
378
+ const authoredDirectories = new Set(files.filter((file) => file.isDirectory()).map((file) => file.name));
379
+ for (const file of files) {
380
+ const id = file.isDirectory() ? file.name : file.name.endsWith(".md") ? path2.basename(file.name, ".md") : null;
381
+ if (id && (file.isDirectory() || !authoredDirectories.has(id))) {
382
+ try {
383
+ const root = file.isDirectory() ? path2.join(skillsDir, id) : skillsDir;
384
+ const filePath = file.isDirectory() ? path2.join(root, "SKILL.md") : path2.join(skillsDir, file.name);
385
+ if (!fs2.existsSync(filePath))
386
+ continue;
387
+ const content = fs2.readFileSync(filePath, "utf-8");
388
+ skills.push({ id, content, root: file.isDirectory() ? root : undefined, source: "authored" });
389
+ } catch {
390
+ console.error(`[mcpx] Warning: could not read skill ${file.name}`);
391
+ }
392
+ }
393
+ }
394
+ const authoredIds = new Set(skills.map((skill) => skill.id));
395
+ const native = [];
396
+ for (const pkg of Object.values(loadConfig().packages ?? {}))
397
+ for (const skill of pkg.skills ?? []) {
398
+ const root = path2.resolve(pkg.root, skill.path);
399
+ if (!root.startsWith(path2.resolve(pkg.root) + path2.sep))
400
+ continue;
401
+ const file = path2.join(root, "SKILL.md");
402
+ if (fs2.existsSync(file))
403
+ native.push({ id: skill.id, root, content: fs2.readFileSync(file, "utf8"), projectionPath: skill.projectionPath, scope: pkg.scope, ownerHarness: pkg.harness, source: "installed", readOnly: true });
404
+ }
405
+ return [...skills, ...installedSkills().filter((skill) => !authoredIds.has(skill.id)), ...native];
406
+ }
407
+ function getSkill(id) {
408
+ validateSkillId(id);
409
+ const skillsDir = getSkillsDir();
410
+ const directory = path2.join(skillsDir, id);
411
+ const packagePath = path2.join(directory, "SKILL.md");
412
+ const legacyPath = path2.join(skillsDir, `${id}.md`);
413
+ const filePath = fs2.existsSync(packagePath) ? packagePath : legacyPath;
414
+ if (!fs2.existsSync(filePath)) {
415
+ const installedPath = path2.join(getSkillPackagesRoot(), id, "SKILL.md");
416
+ if (!fs2.existsSync(installedPath))
417
+ return listSkills().find((skill) => skill.id === id) ?? null;
418
+ const installedRoot = path2.dirname(installedPath);
419
+ return { id, content: fs2.readFileSync(installedPath, "utf8"), root: installedRoot, source: "installed", readOnly: true };
420
+ }
421
+ const content = fs2.readFileSync(filePath, "utf-8");
422
+ return { id, content, root: fs2.existsSync(packagePath) ? directory : undefined, source: "authored" };
423
+ }
424
+ function saveSkill(id, content) {
425
+ validateSkillId(id);
426
+ const skillsDir = getSkillsDir();
427
+ ensureDir(skillsDir);
428
+ const directory = path2.join(skillsDir, id);
429
+ const filePath = path2.join(directory, "SKILL.md");
430
+ ensureDir(directory);
431
+ fs2.writeFileSync(filePath, content, "utf-8");
432
+ const legacyPath = path2.join(skillsDir, `${id}.md`);
433
+ if (fs2.existsSync(legacyPath)) {
434
+ if (fs2.readFileSync(filePath, "utf-8") === fs2.readFileSync(legacyPath, "utf-8"))
435
+ fs2.unlinkSync(legacyPath);
436
+ else
437
+ console.error(`[mcpx] Warning: preserving conflicting legacy skill file: ${legacyPath}`);
438
+ }
439
+ }
440
+ function deleteSkill(id) {
441
+ validateSkillId(id);
442
+ const skillsDir = getSkillsDir();
443
+ const directory = path2.join(skillsDir, id);
444
+ const filePath = path2.join(directory, "SKILL.md");
445
+ const legacyPath = path2.join(skillsDir, `${id}.md`);
446
+ const hadAuthored = fs2.existsSync(directory) || fs2.existsSync(legacyPath);
447
+ if (fs2.existsSync(directory))
448
+ fs2.rmSync(directory, { recursive: true, force: true });
449
+ if (fs2.existsSync(legacyPath))
450
+ fs2.unlinkSync(legacyPath);
451
+ if (!hadAuthored) {
452
+ const installedPath = path2.join(getSkillPackagesRoot(), id);
453
+ if (fs2.existsSync(installedPath))
454
+ fs2.rmSync(installedPath, { recursive: true, force: true });
455
+ const state = readSkillState();
456
+ delete state[id];
457
+ writeSkillState(state);
458
+ }
459
+ }
460
+ function customizeSkill(id, targetId = id) {
461
+ validateSkillId(id);
462
+ validateSkillId(targetId);
463
+ const existing = getSkill(id);
464
+ if (!existing)
465
+ throw new Error(`Skill "${id}" not found`);
466
+ saveSkill(targetId, existing.content);
467
+ return getSkill(targetId);
468
+ }
469
+ async function installSkillFromSource(source, skillName) {
470
+ const parsed = parseSource(source);
471
+ const cache = new PluginCache;
472
+ const cached = await cache.fetch(parsed, skillName ?? "skill");
473
+ const candidates = [];
474
+ const declaredName = (root) => {
475
+ try {
476
+ const content = fs2.readFileSync(path2.join(root, "SKILL.md"), "utf8");
477
+ const frontmatter = content.match(/^---\s*[\s\S]*?^name:\s*([^\r\n]+)[\s\S]*?^---/m)?.[1]?.trim();
478
+ return frontmatter && /^[a-z0-9][a-z0-9-]*$/.test(frontmatter) ? frontmatter : undefined;
479
+ } catch {
480
+ return;
481
+ }
482
+ };
483
+ const addCandidate = (root, id = path2.basename(root)) => {
484
+ if (fs2.existsSync(path2.join(root, "SKILL.md"))) {
485
+ validateSkillPackage(root);
486
+ candidates.push({ id: declaredName(root) ?? id, root });
487
+ }
488
+ };
489
+ addCandidate(cached.root);
490
+ const skillsRoot = path2.join(cached.root, "skills");
491
+ if (fs2.existsSync(skillsRoot)) {
492
+ for (const entry of fs2.readdirSync(skillsRoot, { withFileTypes: true })) {
493
+ if (entry.isDirectory())
494
+ addCandidate(path2.join(skillsRoot, entry.name), entry.name);
495
+ }
496
+ }
497
+ if (candidates.length === 0)
498
+ throw new Error(`No Agent Skill package found in ${source}`);
499
+ const selected = skillName ? candidates.find((candidate) => candidate.id === skillName) : candidates[0];
500
+ if (!selected)
501
+ throw new Error(`Skill "${skillName}" was not found in ${source}`);
502
+ validateSkillId(selected.id);
503
+ const destination = path2.join(getSkillPackagesRoot(), selected.id);
504
+ ensureDir(path2.dirname(destination));
505
+ const state = readSkillState();
506
+ const stateSource = parsed.type === "local" ? path2.resolve(source) : source;
507
+ if (fs2.existsSync(destination)) {
508
+ if (state[selected.id]?.pinned)
509
+ throw new Error(`Skill "${selected.id}" is pinned; unpin it before updating`);
510
+ const previousPath = `${destination}.previous`;
511
+ if (fs2.existsSync(previousPath))
512
+ fs2.rmSync(previousPath, { recursive: true, force: true });
513
+ fs2.renameSync(destination, previousPath);
514
+ state[selected.id] = { ...state[selected.id], source: stateSource, skillName: selected.id, pinned: false, previousPath };
515
+ } else {
516
+ state[selected.id] = { source: stateSource, skillName: selected.id, pinned: false };
517
+ }
518
+ fs2.cpSync(selected.root, destination, { recursive: true, dereference: true });
519
+ writeSkillState(state);
520
+ return { id: selected.id, content: fs2.readFileSync(path2.join(destination, "SKILL.md"), "utf8"), root: destination, source: "installed", readOnly: true };
521
+ }
522
+ function pinSkill(id) {
523
+ const state = readSkillState();
524
+ if (!state[id])
525
+ throw new Error(`Skill "${id}" is not installed`);
526
+ state[id].pinned = true;
527
+ writeSkillState(state);
528
+ }
529
+ function unpinSkill(id) {
530
+ const state = readSkillState();
531
+ if (!state[id])
532
+ throw new Error(`Skill "${id}" is not installed`);
533
+ state[id].pinned = false;
534
+ writeSkillState(state);
535
+ }
536
+ async function updateSkill(id) {
537
+ const state = readSkillState();
538
+ const entry = state[id];
539
+ if (!entry)
540
+ throw new Error(`Skill "${id}" is not installed`);
541
+ if (entry.pinned)
542
+ throw new Error(`Skill "${id}" is pinned; unpin it before updating`);
543
+ return installSkillFromSource(entry.source, entry.skillName ?? id);
544
+ }
545
+ function rollbackSkill(id) {
546
+ const state = readSkillState();
547
+ const entry = state[id];
548
+ if (!entry?.previousPath || !fs2.existsSync(entry.previousPath))
549
+ throw new Error(`Skill "${id}" has no previous revision`);
550
+ const destination = path2.join(getSkillPackagesRoot(), id);
551
+ const failedPath = `${destination}.failed`;
552
+ if (fs2.existsSync(failedPath))
553
+ fs2.rmSync(failedPath, { recursive: true, force: true });
554
+ fs2.renameSync(destination, failedPath);
555
+ fs2.renameSync(entry.previousPath, destination);
556
+ entry.previousPath = failedPath;
557
+ writeSkillState(state);
558
+ return { id, content: fs2.readFileSync(path2.join(destination, "SKILL.md"), "utf8"), root: destination, source: "installed", readOnly: true };
559
+ }
560
+
561
+ export { validateSkillId, normalizePluginName, parseSource, PluginCache, validateSkillPackage, listSkills, getSkill, saveSkill, deleteSkill, customizeSkill, installSkillFromSource, pinSkill, unpinSkill, updateSkill, rollbackSkill };
562
+
563
+ //# debugId=43A9FAB87FA4420064756E2164756E21