@synapsync/synk 1.0.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/dist/cli.mjs ADDED
@@ -0,0 +1,4066 @@
1
+ #!/usr/bin/env node
2
+ import { r as __toESM } from "./_chunks/rolldown-runtime.mjs";
3
+ import { d as require_picocolors, l as pD } from "./_chunks/libs/@clack/core.mjs";
4
+ import "./_chunks/libs/cli-cursor.mjs";
5
+ import { t as ora } from "./_chunks/libs/ora.mjs";
6
+ import { n as ve, r as ye, t as fe } from "./_chunks/libs/@clack/prompts.mjs";
7
+ import { t as xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
8
+ import "./_chunks/libs/@kwsites/file-exists.mjs";
9
+ import "./_chunks/libs/@kwsites/promise-deferred.mjs";
10
+ import { t as esm_default } from "./_chunks/libs/simple-git.mjs";
11
+ import { t as require_gray_matter } from "./_chunks/libs/gray-matter.mjs";
12
+ import "./_chunks/libs/extend-shallow.mjs";
13
+ import "./_chunks/libs/esprima.mjs";
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
15
+ import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
16
+ import { fileURLToPath } from "url";
17
+ import { homedir, platform, tmpdir } from "os";
18
+ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, rm, stat, symlink, writeFile } from "fs/promises";
19
+ import "crypto";
20
+ import { execSync, spawnSync } from "child_process";
21
+ import * as readline from "readline";
22
+ import { Writable } from "stream";
23
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
24
+ const logger = {
25
+ log(message = "") {
26
+ console.log(message);
27
+ },
28
+ line() {
29
+ console.log();
30
+ },
31
+ clear() {
32
+ console.clear();
33
+ },
34
+ info(message) {
35
+ console.log(`${import_picocolors.default.blue("ℹ")} ${message}`);
36
+ },
37
+ success(message) {
38
+ console.log(`${import_picocolors.default.green("✓")} ${message}`);
39
+ },
40
+ warning(message) {
41
+ console.log(`${import_picocolors.default.yellow("⚠")} ${message}`);
42
+ },
43
+ error(message) {
44
+ console.log(`${import_picocolors.default.red("✖")} ${message}`);
45
+ },
46
+ debug(message) {
47
+ if (process.env["DEBUG"]) console.log(`${import_picocolors.default.dim("⋯")} ${import_picocolors.default.dim(message)}`);
48
+ },
49
+ bold(message) {
50
+ console.log(import_picocolors.default.bold(message));
51
+ },
52
+ dim(message) {
53
+ console.log(import_picocolors.default.dim(message));
54
+ },
55
+ section(title) {
56
+ console.log();
57
+ console.log(import_picocolors.default.bold(import_picocolors.default.cyan(title)));
58
+ },
59
+ header(title) {
60
+ console.log();
61
+ console.log(import_picocolors.default.bold(import_picocolors.default.cyan(` ${title}`)));
62
+ console.log(import_picocolors.default.dim(" " + "─".repeat(title.length + 2)));
63
+ console.log();
64
+ },
65
+ list(items) {
66
+ items.forEach((item) => console.log(`${import_picocolors.default.dim(" •")} ${item}`));
67
+ },
68
+ label(key, value) {
69
+ console.log(`${import_picocolors.default.dim(`${key}:`)}${" ".repeat(Math.max(1, 10 - key.length))}${value}`);
70
+ },
71
+ hint(message) {
72
+ console.log(import_picocolors.default.dim(message));
73
+ },
74
+ command(cmd, description) {
75
+ console.log(` ${import_picocolors.default.dim("$")} ${import_picocolors.default.white(cmd)} ${import_picocolors.default.dim(description)}`);
76
+ },
77
+ gradient(line, color) {
78
+ console.log(`${color}${line}`);
79
+ },
80
+ spinner(text) {
81
+ const s = ora({ stream: process.stdout });
82
+ if (text) s.start(text);
83
+ return s;
84
+ },
85
+ note(content, title) {
86
+ console.log();
87
+ if (title) {
88
+ console.log(` ${import_picocolors.default.bold(title)}`);
89
+ console.log();
90
+ }
91
+ for (const line of content.split("\n")) console.log(` ${line}`);
92
+ console.log();
93
+ },
94
+ intro(title) {
95
+ console.log();
96
+ console.log(import_picocolors.default.bgCyan(import_picocolors.default.black(` ${title} `)));
97
+ },
98
+ outro(message) {
99
+ console.log();
100
+ console.log(message);
101
+ console.log();
102
+ },
103
+ cancel(message) {
104
+ console.log(`${import_picocolors.default.yellow("◆")} ${message}`);
105
+ },
106
+ step(message) {
107
+ console.log(`${import_picocolors.default.cyan("◆")} ${message}`);
108
+ },
109
+ message(message) {
110
+ console.log(` ${message}`);
111
+ }
112
+ };
113
+ function getOwnerRepo(parsed) {
114
+ if (parsed.type === "local") return null;
115
+ if (!parsed.url.startsWith("http://") && !parsed.url.startsWith("https://")) return null;
116
+ try {
117
+ let path = new URL(parsed.url).pathname.slice(1);
118
+ path = path.replace(/\.git$/, "");
119
+ if (path.includes("/")) return path;
120
+ } catch {}
121
+ return null;
122
+ }
123
+ function parseOwnerRepo(ownerRepo) {
124
+ const match = ownerRepo.match(/^([^/]+)\/([^/]+)$/);
125
+ if (match) return {
126
+ owner: match[1],
127
+ repo: match[2]
128
+ };
129
+ return null;
130
+ }
131
+ async function isRepoPrivate(owner, repo) {
132
+ try {
133
+ const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
134
+ if (!res.ok) return null;
135
+ return (await res.json()).private === true;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ function isLocalPath(input) {
141
+ return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
142
+ }
143
+ function isDirectSkillUrl(input) {
144
+ if (!input.startsWith("http://") && !input.startsWith("https://")) return false;
145
+ if (!input.toLowerCase().endsWith("/skill.md")) return false;
146
+ if (input.includes("github.com/") && !input.includes("raw.githubusercontent.com")) {
147
+ if (!input.includes("/blob/") && !input.includes("/raw/")) return false;
148
+ }
149
+ if (input.includes("gitlab.com/") && !input.includes("/-/raw/")) return false;
150
+ return true;
151
+ }
152
+ function parseSource(input) {
153
+ if (isLocalPath(input)) {
154
+ const resolvedPath = resolve(input);
155
+ return {
156
+ type: "local",
157
+ url: resolvedPath,
158
+ localPath: resolvedPath
159
+ };
160
+ }
161
+ if (isDirectSkillUrl(input)) return {
162
+ type: "direct-url",
163
+ url: input
164
+ };
165
+ const githubTreeWithPathMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
166
+ if (githubTreeWithPathMatch) {
167
+ const [, owner, repo, ref, subpath] = githubTreeWithPathMatch;
168
+ return {
169
+ type: "github",
170
+ url: `https://github.com/${owner}/${repo}.git`,
171
+ ref,
172
+ subpath
173
+ };
174
+ }
175
+ const githubTreeMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
176
+ if (githubTreeMatch) {
177
+ const [, owner, repo, ref] = githubTreeMatch;
178
+ return {
179
+ type: "github",
180
+ url: `https://github.com/${owner}/${repo}.git`,
181
+ ref
182
+ };
183
+ }
184
+ const githubRepoMatch = input.match(/github\.com\/([^/]+)\/([^/]+)/);
185
+ if (githubRepoMatch) {
186
+ const [, owner, repo] = githubRepoMatch;
187
+ return {
188
+ type: "github",
189
+ url: `https://github.com/${owner}/${repo.replace(/\.git$/, "")}.git`
190
+ };
191
+ }
192
+ const gitlabTreeWithPathMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)\/(.+)/);
193
+ if (gitlabTreeWithPathMatch) {
194
+ const [, protocol, hostname, repoPath, ref, subpath] = gitlabTreeWithPathMatch;
195
+ if (hostname !== "github.com" && repoPath) return {
196
+ type: "gitlab",
197
+ url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
198
+ ref,
199
+ subpath
200
+ };
201
+ }
202
+ const gitlabTreeMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)$/);
203
+ if (gitlabTreeMatch) {
204
+ const [, protocol, hostname, repoPath, ref] = gitlabTreeMatch;
205
+ if (hostname !== "github.com" && repoPath) return {
206
+ type: "gitlab",
207
+ url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
208
+ ref
209
+ };
210
+ }
211
+ const gitlabRepoMatch = input.match(/gitlab\.com\/(.+?)(?:\.git)?\/?$/);
212
+ if (gitlabRepoMatch) {
213
+ const repoPath = gitlabRepoMatch[1];
214
+ if (repoPath.includes("/")) return {
215
+ type: "gitlab",
216
+ url: `https://gitlab.com/${repoPath}.git`
217
+ };
218
+ }
219
+ const atSkillMatch = input.match(/^([^/]+)\/([^/@]+)@(.+)$/);
220
+ if (atSkillMatch && !input.includes(":") && !input.startsWith(".") && !input.startsWith("/")) {
221
+ const [, owner, repo, skillFilter] = atSkillMatch;
222
+ return {
223
+ type: "github",
224
+ url: `https://github.com/${owner}/${repo}.git`,
225
+ skillFilter
226
+ };
227
+ }
228
+ const shorthandMatch = input.match(/^([^/]+)\/([^/]+)(?:\/(.+))?$/);
229
+ if (shorthandMatch && !input.includes(":") && !input.startsWith(".") && !input.startsWith("/")) {
230
+ const [, owner, repo, subpath] = shorthandMatch;
231
+ return {
232
+ type: "github",
233
+ url: `https://github.com/${owner}/${repo}.git`,
234
+ subpath
235
+ };
236
+ }
237
+ if (isWellKnownUrl(input)) return {
238
+ type: "well-known",
239
+ url: input
240
+ };
241
+ return {
242
+ type: "git",
243
+ url: input
244
+ };
245
+ }
246
+ function isWellKnownUrl(input) {
247
+ if (!input.startsWith("http://") && !input.startsWith("https://")) return false;
248
+ try {
249
+ const parsed = new URL(input);
250
+ if ([
251
+ "github.com",
252
+ "gitlab.com",
253
+ "huggingface.co",
254
+ "raw.githubusercontent.com"
255
+ ].includes(parsed.hostname)) return false;
256
+ if (input.toLowerCase().endsWith("/skill.md")) return false;
257
+ if (input.endsWith(".git")) return false;
258
+ return true;
259
+ } catch {
260
+ return false;
261
+ }
262
+ }
263
+ const home = homedir();
264
+ const configHome = xdgConfig ?? join(home, ".config");
265
+ const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
266
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
267
+ const agents = {
268
+ amp: {
269
+ name: "amp",
270
+ displayName: "Amp",
271
+ skillsDir: ".agents/skills",
272
+ globalSkillsDir: join(configHome, "agents/skills"),
273
+ agentsDir: ".agents/agents",
274
+ globalAgentsDir: join(configHome, "agents/agents"),
275
+ promptsDir: ".agents/prompts",
276
+ globalPromptsDir: join(configHome, "agents/prompts"),
277
+ detectInstalled: async () => {
278
+ return existsSync(join(configHome, "amp"));
279
+ }
280
+ },
281
+ antigravity: {
282
+ name: "antigravity",
283
+ displayName: "Antigravity",
284
+ skillsDir: ".agent/skills",
285
+ globalSkillsDir: join(home, ".gemini/antigravity/skills"),
286
+ agentsDir: ".agent/agents",
287
+ globalAgentsDir: join(home, ".gemini/antigravity/agents"),
288
+ promptsDir: ".agent/prompts",
289
+ globalPromptsDir: join(home, ".gemini/antigravity/prompts"),
290
+ detectInstalled: async () => {
291
+ return existsSync(join(process.cwd(), ".agent")) || existsSync(join(home, ".gemini/antigravity"));
292
+ }
293
+ },
294
+ augment: {
295
+ name: "augment",
296
+ displayName: "Augment",
297
+ skillsDir: ".augment/skills",
298
+ globalSkillsDir: join(home, ".augment/skills"),
299
+ agentsDir: ".augment/agents",
300
+ globalAgentsDir: join(home, ".augment/agents"),
301
+ promptsDir: ".augment/prompts",
302
+ globalPromptsDir: join(home, ".augment/prompts"),
303
+ detectInstalled: async () => {
304
+ return existsSync(join(home, ".augment"));
305
+ }
306
+ },
307
+ "claude-code": {
308
+ name: "claude-code",
309
+ displayName: "Claude Code",
310
+ skillsDir: ".claude/skills",
311
+ globalSkillsDir: join(claudeHome, "skills"),
312
+ agentsDir: ".claude/agents",
313
+ globalAgentsDir: join(claudeHome, "agents"),
314
+ promptsDir: ".claude/prompts",
315
+ globalPromptsDir: join(claudeHome, "prompts"),
316
+ detectInstalled: async () => {
317
+ return existsSync(claudeHome);
318
+ }
319
+ },
320
+ openclaw: {
321
+ name: "openclaw",
322
+ displayName: "OpenClaw",
323
+ skillsDir: "skills",
324
+ globalSkillsDir: existsSync(join(home, ".openclaw")) ? join(home, ".openclaw/skills") : existsSync(join(home, ".clawdbot")) ? join(home, ".clawdbot/skills") : join(home, ".moltbot/skills"),
325
+ agentsDir: "agents",
326
+ globalAgentsDir: existsSync(join(home, ".openclaw")) ? join(home, ".openclaw/agents") : existsSync(join(home, ".clawdbot")) ? join(home, ".clawdbot/agents") : join(home, ".moltbot/agents"),
327
+ promptsDir: "prompts",
328
+ globalPromptsDir: existsSync(join(home, ".openclaw")) ? join(home, ".openclaw/prompts") : existsSync(join(home, ".clawdbot")) ? join(home, ".clawdbot/prompts") : join(home, ".moltbot/prompts"),
329
+ detectInstalled: async () => {
330
+ return existsSync(join(home, ".openclaw")) || existsSync(join(home, ".clawdbot")) || existsSync(join(home, ".moltbot"));
331
+ }
332
+ },
333
+ cline: {
334
+ name: "cline",
335
+ displayName: "Cline",
336
+ skillsDir: ".cline/skills",
337
+ globalSkillsDir: join(home, ".cline/skills"),
338
+ agentsDir: ".cline/agents",
339
+ globalAgentsDir: join(home, ".cline/agents"),
340
+ promptsDir: ".cline/prompts",
341
+ globalPromptsDir: join(home, ".cline/prompts"),
342
+ detectInstalled: async () => {
343
+ return existsSync(join(home, ".cline"));
344
+ }
345
+ },
346
+ codebuddy: {
347
+ name: "codebuddy",
348
+ displayName: "CodeBuddy",
349
+ skillsDir: ".codebuddy/skills",
350
+ globalSkillsDir: join(home, ".codebuddy/skills"),
351
+ agentsDir: ".codebuddy/agents",
352
+ globalAgentsDir: join(home, ".codebuddy/agents"),
353
+ promptsDir: ".codebuddy/prompts",
354
+ globalPromptsDir: join(home, ".codebuddy/prompts"),
355
+ detectInstalled: async () => {
356
+ return existsSync(join(process.cwd(), ".codebuddy")) || existsSync(join(home, ".codebuddy"));
357
+ }
358
+ },
359
+ codex: {
360
+ name: "codex",
361
+ displayName: "Codex",
362
+ skillsDir: ".agents/skills",
363
+ globalSkillsDir: join(codexHome, "skills"),
364
+ agentsDir: ".agents/agents",
365
+ globalAgentsDir: join(codexHome, "agents"),
366
+ promptsDir: ".agents/prompts",
367
+ globalPromptsDir: join(codexHome, "prompts"),
368
+ detectInstalled: async () => {
369
+ return existsSync(codexHome) || existsSync("/etc/codex");
370
+ }
371
+ },
372
+ "command-code": {
373
+ name: "command-code",
374
+ displayName: "Command Code",
375
+ skillsDir: ".commandcode/skills",
376
+ globalSkillsDir: join(home, ".commandcode/skills"),
377
+ agentsDir: ".commandcode/agents",
378
+ globalAgentsDir: join(home, ".commandcode/agents"),
379
+ promptsDir: ".commandcode/prompts",
380
+ globalPromptsDir: join(home, ".commandcode/prompts"),
381
+ detectInstalled: async () => {
382
+ return existsSync(join(home, ".commandcode"));
383
+ }
384
+ },
385
+ continue: {
386
+ name: "continue",
387
+ displayName: "Continue",
388
+ skillsDir: ".continue/skills",
389
+ globalSkillsDir: join(home, ".continue/skills"),
390
+ agentsDir: ".continue/agents",
391
+ globalAgentsDir: join(home, ".continue/agents"),
392
+ promptsDir: ".continue/prompts",
393
+ globalPromptsDir: join(home, ".continue/prompts"),
394
+ detectInstalled: async () => {
395
+ return existsSync(join(process.cwd(), ".continue")) || existsSync(join(home, ".continue"));
396
+ }
397
+ },
398
+ crush: {
399
+ name: "crush",
400
+ displayName: "Crush",
401
+ skillsDir: ".crush/skills",
402
+ globalSkillsDir: join(home, ".config/crush/skills"),
403
+ agentsDir: ".crush/agents",
404
+ globalAgentsDir: join(home, ".config/crush/agents"),
405
+ promptsDir: ".crush/prompts",
406
+ globalPromptsDir: join(home, ".config/crush/prompts"),
407
+ detectInstalled: async () => {
408
+ return existsSync(join(home, ".config/crush"));
409
+ }
410
+ },
411
+ cursor: {
412
+ name: "cursor",
413
+ displayName: "Cursor",
414
+ skillsDir: ".cursor/skills",
415
+ globalSkillsDir: join(home, ".cursor/skills"),
416
+ agentsDir: ".cursor/agents",
417
+ globalAgentsDir: join(home, ".cursor/agents"),
418
+ promptsDir: ".cursor/prompts",
419
+ globalPromptsDir: join(home, ".cursor/prompts"),
420
+ detectInstalled: async () => {
421
+ return existsSync(join(home, ".cursor"));
422
+ }
423
+ },
424
+ droid: {
425
+ name: "droid",
426
+ displayName: "Droid",
427
+ skillsDir: ".factory/skills",
428
+ globalSkillsDir: join(home, ".factory/skills"),
429
+ agentsDir: ".factory/agents",
430
+ globalAgentsDir: join(home, ".factory/agents"),
431
+ promptsDir: ".factory/prompts",
432
+ globalPromptsDir: join(home, ".factory/prompts"),
433
+ detectInstalled: async () => {
434
+ return existsSync(join(home, ".factory"));
435
+ }
436
+ },
437
+ "gemini-cli": {
438
+ name: "gemini-cli",
439
+ displayName: "Gemini CLI",
440
+ skillsDir: ".agents/skills",
441
+ globalSkillsDir: join(home, ".gemini/skills"),
442
+ agentsDir: ".agents/agents",
443
+ globalAgentsDir: join(home, ".gemini/agents"),
444
+ promptsDir: ".agents/prompts",
445
+ globalPromptsDir: join(home, ".gemini/prompts"),
446
+ detectInstalled: async () => {
447
+ return existsSync(join(home, ".gemini"));
448
+ }
449
+ },
450
+ "github-copilot": {
451
+ name: "github-copilot",
452
+ displayName: "GitHub Copilot",
453
+ skillsDir: ".agents/skills",
454
+ globalSkillsDir: join(home, ".copilot/skills"),
455
+ agentsDir: ".agents/agents",
456
+ globalAgentsDir: join(home, ".copilot/agents"),
457
+ promptsDir: ".agents/prompts",
458
+ globalPromptsDir: join(home, ".copilot/prompts"),
459
+ detectInstalled: async () => {
460
+ return existsSync(join(process.cwd(), ".github")) || existsSync(join(home, ".copilot"));
461
+ }
462
+ },
463
+ goose: {
464
+ name: "goose",
465
+ displayName: "Goose",
466
+ skillsDir: ".goose/skills",
467
+ globalSkillsDir: join(configHome, "goose/skills"),
468
+ agentsDir: ".goose/agents",
469
+ globalAgentsDir: join(configHome, "goose/agents"),
470
+ promptsDir: ".goose/prompts",
471
+ globalPromptsDir: join(configHome, "goose/prompts"),
472
+ detectInstalled: async () => {
473
+ return existsSync(join(configHome, "goose"));
474
+ }
475
+ },
476
+ junie: {
477
+ name: "junie",
478
+ displayName: "Junie",
479
+ skillsDir: ".junie/skills",
480
+ globalSkillsDir: join(home, ".junie/skills"),
481
+ agentsDir: ".junie/agents",
482
+ globalAgentsDir: join(home, ".junie/agents"),
483
+ promptsDir: ".junie/prompts",
484
+ globalPromptsDir: join(home, ".junie/prompts"),
485
+ detectInstalled: async () => {
486
+ return existsSync(join(home, ".junie"));
487
+ }
488
+ },
489
+ "iflow-cli": {
490
+ name: "iflow-cli",
491
+ displayName: "iFlow CLI",
492
+ skillsDir: ".iflow/skills",
493
+ globalSkillsDir: join(home, ".iflow/skills"),
494
+ agentsDir: ".iflow/agents",
495
+ globalAgentsDir: join(home, ".iflow/agents"),
496
+ promptsDir: ".iflow/prompts",
497
+ globalPromptsDir: join(home, ".iflow/prompts"),
498
+ detectInstalled: async () => {
499
+ return existsSync(join(home, ".iflow"));
500
+ }
501
+ },
502
+ kilo: {
503
+ name: "kilo",
504
+ displayName: "Kilo Code",
505
+ skillsDir: ".kilocode/skills",
506
+ globalSkillsDir: join(home, ".kilocode/skills"),
507
+ agentsDir: ".kilocode/agents",
508
+ globalAgentsDir: join(home, ".kilocode/agents"),
509
+ promptsDir: ".kilocode/prompts",
510
+ globalPromptsDir: join(home, ".kilocode/prompts"),
511
+ detectInstalled: async () => {
512
+ return existsSync(join(home, ".kilocode"));
513
+ }
514
+ },
515
+ "kimi-cli": {
516
+ name: "kimi-cli",
517
+ displayName: "Kimi Code CLI",
518
+ skillsDir: ".agents/skills",
519
+ globalSkillsDir: join(home, ".config/agents/skills"),
520
+ agentsDir: ".agents/agents",
521
+ globalAgentsDir: join(home, ".config/agents/agents"),
522
+ promptsDir: ".agents/prompts",
523
+ globalPromptsDir: join(home, ".config/agents/prompts"),
524
+ detectInstalled: async () => {
525
+ return existsSync(join(home, ".kimi"));
526
+ }
527
+ },
528
+ "kiro-cli": {
529
+ name: "kiro-cli",
530
+ displayName: "Kiro CLI",
531
+ skillsDir: ".kiro/skills",
532
+ globalSkillsDir: join(home, ".kiro/skills"),
533
+ agentsDir: ".kiro/agents",
534
+ globalAgentsDir: join(home, ".kiro/agents"),
535
+ promptsDir: ".kiro/prompts",
536
+ globalPromptsDir: join(home, ".kiro/prompts"),
537
+ detectInstalled: async () => {
538
+ return existsSync(join(home, ".kiro"));
539
+ }
540
+ },
541
+ kode: {
542
+ name: "kode",
543
+ displayName: "Kode",
544
+ skillsDir: ".kode/skills",
545
+ globalSkillsDir: join(home, ".kode/skills"),
546
+ agentsDir: ".kode/agents",
547
+ globalAgentsDir: join(home, ".kode/agents"),
548
+ promptsDir: ".kode/prompts",
549
+ globalPromptsDir: join(home, ".kode/prompts"),
550
+ detectInstalled: async () => {
551
+ return existsSync(join(home, ".kode"));
552
+ }
553
+ },
554
+ mcpjam: {
555
+ name: "mcpjam",
556
+ displayName: "MCPJam",
557
+ skillsDir: ".mcpjam/skills",
558
+ globalSkillsDir: join(home, ".mcpjam/skills"),
559
+ agentsDir: ".mcpjam/agents",
560
+ globalAgentsDir: join(home, ".mcpjam/agents"),
561
+ promptsDir: ".mcpjam/prompts",
562
+ globalPromptsDir: join(home, ".mcpjam/prompts"),
563
+ detectInstalled: async () => {
564
+ return existsSync(join(home, ".mcpjam"));
565
+ }
566
+ },
567
+ "mistral-vibe": {
568
+ name: "mistral-vibe",
569
+ displayName: "Mistral Vibe",
570
+ skillsDir: ".vibe/skills",
571
+ globalSkillsDir: join(home, ".vibe/skills"),
572
+ agentsDir: ".vibe/agents",
573
+ globalAgentsDir: join(home, ".vibe/agents"),
574
+ promptsDir: ".vibe/prompts",
575
+ globalPromptsDir: join(home, ".vibe/prompts"),
576
+ detectInstalled: async () => {
577
+ return existsSync(join(home, ".vibe"));
578
+ }
579
+ },
580
+ mux: {
581
+ name: "mux",
582
+ displayName: "Mux",
583
+ skillsDir: ".mux/skills",
584
+ globalSkillsDir: join(home, ".mux/skills"),
585
+ agentsDir: ".mux/agents",
586
+ globalAgentsDir: join(home, ".mux/agents"),
587
+ promptsDir: ".mux/prompts",
588
+ globalPromptsDir: join(home, ".mux/prompts"),
589
+ detectInstalled: async () => {
590
+ return existsSync(join(home, ".mux"));
591
+ }
592
+ },
593
+ opencode: {
594
+ name: "opencode",
595
+ displayName: "OpenCode",
596
+ skillsDir: ".agents/skills",
597
+ globalSkillsDir: join(configHome, "opencode/skills"),
598
+ agentsDir: ".agents/agents",
599
+ globalAgentsDir: join(configHome, "opencode/agents"),
600
+ promptsDir: ".agents/prompts",
601
+ globalPromptsDir: join(configHome, "opencode/prompts"),
602
+ detectInstalled: async () => {
603
+ return existsSync(join(configHome, "opencode")) || existsSync(join(claudeHome, "skills"));
604
+ }
605
+ },
606
+ openhands: {
607
+ name: "openhands",
608
+ displayName: "OpenHands",
609
+ skillsDir: ".openhands/skills",
610
+ globalSkillsDir: join(home, ".openhands/skills"),
611
+ agentsDir: ".openhands/agents",
612
+ globalAgentsDir: join(home, ".openhands/agents"),
613
+ promptsDir: ".openhands/prompts",
614
+ globalPromptsDir: join(home, ".openhands/prompts"),
615
+ detectInstalled: async () => {
616
+ return existsSync(join(home, ".openhands"));
617
+ }
618
+ },
619
+ pi: {
620
+ name: "pi",
621
+ displayName: "Pi",
622
+ skillsDir: ".pi/skills",
623
+ globalSkillsDir: join(home, ".pi/agent/skills"),
624
+ agentsDir: ".pi/agents",
625
+ globalAgentsDir: join(home, ".pi/agent/agents"),
626
+ promptsDir: ".pi/prompts",
627
+ globalPromptsDir: join(home, ".pi/agent/prompts"),
628
+ detectInstalled: async () => {
629
+ return existsSync(join(home, ".pi/agent"));
630
+ }
631
+ },
632
+ qoder: {
633
+ name: "qoder",
634
+ displayName: "Qoder",
635
+ skillsDir: ".qoder/skills",
636
+ globalSkillsDir: join(home, ".qoder/skills"),
637
+ agentsDir: ".qoder/agents",
638
+ globalAgentsDir: join(home, ".qoder/agents"),
639
+ promptsDir: ".qoder/prompts",
640
+ globalPromptsDir: join(home, ".qoder/prompts"),
641
+ detectInstalled: async () => {
642
+ return existsSync(join(home, ".qoder"));
643
+ }
644
+ },
645
+ "qwen-code": {
646
+ name: "qwen-code",
647
+ displayName: "Qwen Code",
648
+ skillsDir: ".qwen/skills",
649
+ globalSkillsDir: join(home, ".qwen/skills"),
650
+ agentsDir: ".qwen/agents",
651
+ globalAgentsDir: join(home, ".qwen/agents"),
652
+ promptsDir: ".qwen/prompts",
653
+ globalPromptsDir: join(home, ".qwen/prompts"),
654
+ detectInstalled: async () => {
655
+ return existsSync(join(home, ".qwen"));
656
+ }
657
+ },
658
+ replit: {
659
+ name: "replit",
660
+ displayName: "Replit",
661
+ skillsDir: ".agents/skills",
662
+ globalSkillsDir: join(configHome, "agents/skills"),
663
+ agentsDir: ".agents/agents",
664
+ globalAgentsDir: join(configHome, "agents/agents"),
665
+ promptsDir: ".agents/prompts",
666
+ globalPromptsDir: join(configHome, "agents/prompts"),
667
+ showInUniversalList: false,
668
+ detectInstalled: async () => {
669
+ return existsSync(join(process.cwd(), ".agents"));
670
+ }
671
+ },
672
+ roo: {
673
+ name: "roo",
674
+ displayName: "Roo Code",
675
+ skillsDir: ".roo/skills",
676
+ globalSkillsDir: join(home, ".roo/skills"),
677
+ agentsDir: ".roo/agents",
678
+ globalAgentsDir: join(home, ".roo/agents"),
679
+ promptsDir: ".roo/prompts",
680
+ globalPromptsDir: join(home, ".roo/prompts"),
681
+ detectInstalled: async () => {
682
+ return existsSync(join(home, ".roo"));
683
+ }
684
+ },
685
+ trae: {
686
+ name: "trae",
687
+ displayName: "Trae",
688
+ skillsDir: ".trae/skills",
689
+ globalSkillsDir: join(home, ".trae/skills"),
690
+ agentsDir: ".trae/agents",
691
+ globalAgentsDir: join(home, ".trae/agents"),
692
+ promptsDir: ".trae/prompts",
693
+ globalPromptsDir: join(home, ".trae/prompts"),
694
+ detectInstalled: async () => {
695
+ return existsSync(join(home, ".trae"));
696
+ }
697
+ },
698
+ "trae-cn": {
699
+ name: "trae-cn",
700
+ displayName: "Trae CN",
701
+ skillsDir: ".trae/skills",
702
+ globalSkillsDir: join(home, ".trae-cn/skills"),
703
+ agentsDir: ".trae/agents",
704
+ globalAgentsDir: join(home, ".trae-cn/agents"),
705
+ promptsDir: ".trae/prompts",
706
+ globalPromptsDir: join(home, ".trae-cn/prompts"),
707
+ detectInstalled: async () => {
708
+ return existsSync(join(home, ".trae-cn"));
709
+ }
710
+ },
711
+ windsurf: {
712
+ name: "windsurf",
713
+ displayName: "Windsurf",
714
+ skillsDir: ".windsurf/skills",
715
+ globalSkillsDir: join(home, ".codeium/windsurf/skills"),
716
+ agentsDir: ".windsurf/agents",
717
+ globalAgentsDir: join(home, ".codeium/windsurf/agents"),
718
+ promptsDir: ".windsurf/prompts",
719
+ globalPromptsDir: join(home, ".codeium/windsurf/prompts"),
720
+ detectInstalled: async () => {
721
+ return existsSync(join(home, ".codeium/windsurf"));
722
+ }
723
+ },
724
+ zencoder: {
725
+ name: "zencoder",
726
+ displayName: "Zencoder",
727
+ skillsDir: ".zencoder/skills",
728
+ globalSkillsDir: join(home, ".zencoder/skills"),
729
+ agentsDir: ".zencoder/agents",
730
+ globalAgentsDir: join(home, ".zencoder/agents"),
731
+ promptsDir: ".zencoder/prompts",
732
+ globalPromptsDir: join(home, ".zencoder/prompts"),
733
+ detectInstalled: async () => {
734
+ return existsSync(join(home, ".zencoder"));
735
+ }
736
+ },
737
+ neovate: {
738
+ name: "neovate",
739
+ displayName: "Neovate",
740
+ skillsDir: ".neovate/skills",
741
+ globalSkillsDir: join(home, ".neovate/skills"),
742
+ agentsDir: ".neovate/agents",
743
+ globalAgentsDir: join(home, ".neovate/agents"),
744
+ promptsDir: ".neovate/prompts",
745
+ globalPromptsDir: join(home, ".neovate/prompts"),
746
+ detectInstalled: async () => {
747
+ return existsSync(join(home, ".neovate"));
748
+ }
749
+ },
750
+ pochi: {
751
+ name: "pochi",
752
+ displayName: "Pochi",
753
+ skillsDir: ".pochi/skills",
754
+ globalSkillsDir: join(home, ".pochi/skills"),
755
+ agentsDir: ".pochi/agents",
756
+ globalAgentsDir: join(home, ".pochi/agents"),
757
+ promptsDir: ".pochi/prompts",
758
+ globalPromptsDir: join(home, ".pochi/prompts"),
759
+ detectInstalled: async () => {
760
+ return existsSync(join(home, ".pochi"));
761
+ }
762
+ },
763
+ adal: {
764
+ name: "adal",
765
+ displayName: "AdaL",
766
+ skillsDir: ".adal/skills",
767
+ globalSkillsDir: join(home, ".adal/skills"),
768
+ agentsDir: ".adal/agents",
769
+ globalAgentsDir: join(home, ".adal/agents"),
770
+ promptsDir: ".adal/prompts",
771
+ globalPromptsDir: join(home, ".adal/prompts"),
772
+ detectInstalled: async () => {
773
+ return existsSync(join(home, ".adal"));
774
+ }
775
+ }
776
+ };
777
+ async function detectInstalledAgents() {
778
+ return (await Promise.all(Object.entries(agents).map(async ([type, config]) => ({
779
+ type,
780
+ installed: await config.detectInstalled()
781
+ })))).filter((r) => r.installed).map((r) => r.type);
782
+ }
783
+ const AGENTS_DIR$3 = ".agents";
784
+ const COGNITIVE_SUBDIRS = {
785
+ skill: "skills",
786
+ agent: "agents",
787
+ prompt: "prompts"
788
+ };
789
+ const COGNITIVE_FILE_NAMES = {
790
+ skill: "SKILL.md",
791
+ agent: "AGENT.md",
792
+ prompt: "PROMPT.md"
793
+ };
794
+ function getUniversalAgents() {
795
+ return Object.entries(agents).filter(([_, config]) => config.skillsDir === ".agents/skills" && config.showInUniversalList !== false).map(([type]) => type);
796
+ }
797
+ function getNonUniversalAgents() {
798
+ return Object.entries(agents).filter(([_, config]) => config.skillsDir !== ".agents/skills").map(([type]) => type);
799
+ }
800
+ function isUniversalAgent(type) {
801
+ return agents[type].skillsDir === ".agents/skills";
802
+ }
803
+ function getCognitiveDir(agentType, cognitiveType, scope) {
804
+ const agent = agents[agentType];
805
+ if (scope === "global") switch (cognitiveType) {
806
+ case "skill": return agent.globalSkillsDir;
807
+ case "agent": return agent.globalAgentsDir;
808
+ case "prompt": return agent.globalPromptsDir;
809
+ }
810
+ else switch (cognitiveType) {
811
+ case "skill": return agent.skillsDir;
812
+ case "agent": return agent.agentsDir;
813
+ case "prompt": return agent.promptsDir;
814
+ }
815
+ }
816
+ function isUniversalForType(agentType, cognitiveType) {
817
+ const agent = agents[agentType];
818
+ const universalDir = `.agents/${COGNITIVE_SUBDIRS[cognitiveType]}`;
819
+ switch (cognitiveType) {
820
+ case "skill": return agent.skillsDir === universalDir;
821
+ case "agent": return agent.agentsDir === universalDir;
822
+ case "prompt": return agent.promptsDir === universalDir;
823
+ }
824
+ }
825
+ function shortenPath$1(fullPath, cwd) {
826
+ const home = homedir();
827
+ if (fullPath === home || fullPath.startsWith(home + sep)) return "~" + fullPath.slice(home.length);
828
+ if (fullPath === cwd || fullPath.startsWith(cwd + sep)) return "." + fullPath.slice(cwd.length);
829
+ return fullPath;
830
+ }
831
+ function formatList$1(items, maxShow = 5) {
832
+ if (items.length <= maxShow) return items.join(", ");
833
+ const shown = items.slice(0, maxShow);
834
+ const remaining = items.length - maxShow;
835
+ return `${shown.join(", ")} +${remaining} more`;
836
+ }
837
+ function splitAgentsByType(agentTypes) {
838
+ const universal = [];
839
+ const symlinked = [];
840
+ for (const a of agentTypes) if (isUniversalAgent(a)) universal.push(agents[a].displayName);
841
+ else symlinked.push(agents[a].displayName);
842
+ return {
843
+ universal,
844
+ symlinked
845
+ };
846
+ }
847
+ function buildAgentSummaryLines(targetAgents, installMode) {
848
+ const lines = [];
849
+ const { universal, symlinked } = splitAgentsByType(targetAgents);
850
+ if (installMode === "symlink") {
851
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
852
+ if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("symlink →")} ${formatList$1(symlinked)}`);
853
+ } else {
854
+ const allNames = targetAgents.map((a) => agents[a].displayName);
855
+ lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(allNames)}`);
856
+ }
857
+ return lines;
858
+ }
859
+ function ensureUniversalAgents(targetAgents) {
860
+ const universalAgents = getUniversalAgents();
861
+ const result = [...targetAgents];
862
+ for (const ua of universalAgents) if (!result.includes(ua)) result.push(ua);
863
+ return result;
864
+ }
865
+ function buildResultLines(results, targetAgents) {
866
+ const lines = [];
867
+ const { universal, symlinked: symlinkAgents } = splitAgentsByType(targetAgents);
868
+ const successfulSymlinks = results.filter((r) => !r.symlinkFailed && !universal.includes(r.agent)).map((r) => r.agent);
869
+ const failedSymlinks = results.filter((r) => r.symlinkFailed).map((r) => r.agent);
870
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
871
+ if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("symlinked:")} ${formatList$1(successfulSymlinks)}`);
872
+ if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
873
+ return lines;
874
+ }
875
+ const AGENTS_DIR$2 = ".agents";
876
+ const LOCK_FILE$2 = ".skill-lock.json";
877
+ const CURRENT_VERSION = 4;
878
+ function getSkillLockPath$2() {
879
+ return join(homedir(), AGENTS_DIR$2, LOCK_FILE$2);
880
+ }
881
+ async function readSkillLock$2() {
882
+ const lockPath = getSkillLockPath$2();
883
+ try {
884
+ const content = await readFile(lockPath, "utf-8");
885
+ const parsed = JSON.parse(content);
886
+ if (typeof parsed.version !== "number" || !parsed.skills) return createEmptyLockFile();
887
+ if (parsed.version < CURRENT_VERSION) return createEmptyLockFile();
888
+ return parsed;
889
+ } catch (error) {
890
+ return createEmptyLockFile();
891
+ }
892
+ }
893
+ async function writeSkillLock(lock) {
894
+ const lockPath = getSkillLockPath$2();
895
+ await mkdir(dirname(lockPath), { recursive: true });
896
+ await writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
897
+ }
898
+ function getGitHubToken() {
899
+ if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
900
+ if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
901
+ try {
902
+ const token = execSync("gh auth token", {
903
+ encoding: "utf-8",
904
+ stdio: [
905
+ "pipe",
906
+ "pipe",
907
+ "pipe"
908
+ ]
909
+ }).trim();
910
+ if (token) return token;
911
+ } catch {}
912
+ return null;
913
+ }
914
+ async function fetchSkillFolderHash(ownerRepo, skillPath, token) {
915
+ let folderPath = skillPath.replace(/\\/g, "/");
916
+ if (folderPath.endsWith("/SKILL.md")) folderPath = folderPath.slice(0, -9);
917
+ else if (folderPath.endsWith("SKILL.md")) folderPath = folderPath.slice(0, -8);
918
+ if (folderPath.endsWith("/")) folderPath = folderPath.slice(0, -1);
919
+ for (const branch of ["main", "master"]) try {
920
+ const url = `https://api.github.com/repos/${ownerRepo}/git/trees/${branch}?recursive=1`;
921
+ const headers = {
922
+ Accept: "application/vnd.github.v3+json",
923
+ "User-Agent": "skills-cli"
924
+ };
925
+ if (token) headers["Authorization"] = `Bearer ${token}`;
926
+ const response = await fetch(url, { headers });
927
+ if (!response.ok) continue;
928
+ const data = await response.json();
929
+ if (!folderPath) return data.sha;
930
+ const folderEntry = data.tree.find((entry) => entry.type === "tree" && entry.path === folderPath);
931
+ if (folderEntry) return folderEntry.sha;
932
+ } catch {
933
+ continue;
934
+ }
935
+ return null;
936
+ }
937
+ async function addCognitiveToLock(name, cognitiveType, entry) {
938
+ const lock = await readSkillLock$2();
939
+ const now = (/* @__PURE__ */ new Date()).toISOString();
940
+ const existingEntry = lock.skills[name];
941
+ lock.skills[name] = {
942
+ ...entry,
943
+ cognitiveType,
944
+ installedAt: existingEntry?.installedAt ?? now,
945
+ updatedAt: now
946
+ };
947
+ await writeSkillLock(lock);
948
+ }
949
+ async function addSkillToLock(skillName, entry) {
950
+ return addCognitiveToLock(skillName, "skill", entry);
951
+ }
952
+ async function removeSkillFromLock(skillName) {
953
+ const lock = await readSkillLock$2();
954
+ if (!(skillName in lock.skills)) return false;
955
+ delete lock.skills[skillName];
956
+ await writeSkillLock(lock);
957
+ return true;
958
+ }
959
+ async function getSkillFromLock(skillName) {
960
+ return (await readSkillLock$2()).skills[skillName] ?? null;
961
+ }
962
+ function createEmptyLockFile() {
963
+ return {
964
+ version: CURRENT_VERSION,
965
+ skills: {},
966
+ dismissed: {}
967
+ };
968
+ }
969
+ async function isPromptDismissed(promptKey) {
970
+ return (await readSkillLock$2()).dismissed?.[promptKey] === true;
971
+ }
972
+ async function dismissPrompt(promptKey) {
973
+ const lock = await readSkillLock$2();
974
+ if (!lock.dismissed) lock.dismissed = {};
975
+ lock.dismissed[promptKey] = true;
976
+ await writeSkillLock(lock);
977
+ }
978
+ async function getLastSelectedAgents() {
979
+ return (await readSkillLock$2()).lastSelectedAgents;
980
+ }
981
+ async function saveSelectedAgents(agents) {
982
+ const lock = await readSkillLock$2();
983
+ lock.lastSelectedAgents = agents;
984
+ await writeSkillLock(lock);
985
+ }
986
+ const silentOutput = new Writable({ write(_chunk, _encoding, callback) {
987
+ callback();
988
+ } });
989
+ const S_STEP_ACTIVE = import_picocolors.default.green("◆");
990
+ const S_STEP_CANCEL = import_picocolors.default.red("■");
991
+ const S_STEP_SUBMIT = import_picocolors.default.green("◇");
992
+ const S_RADIO_ACTIVE = import_picocolors.default.green("●");
993
+ const S_RADIO_INACTIVE = import_picocolors.default.dim("○");
994
+ const S_CHECKBOX_LOCKED = import_picocolors.default.green("✓");
995
+ const S_BAR = import_picocolors.default.dim("│");
996
+ const S_BAR_H = import_picocolors.default.dim("─");
997
+ const cancelSymbol = Symbol("cancel");
998
+ async function searchMultiselect(options) {
999
+ const { message, items, maxVisible = 8, initialSelected = [], required = false, lockedSection } = options;
1000
+ return new Promise((resolve) => {
1001
+ const rl = readline.createInterface({
1002
+ input: process.stdin,
1003
+ output: silentOutput,
1004
+ terminal: false
1005
+ });
1006
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
1007
+ readline.emitKeypressEvents(process.stdin, rl);
1008
+ let query = "";
1009
+ let cursor = 0;
1010
+ const selected = new Set(initialSelected);
1011
+ let lastRenderHeight = 0;
1012
+ const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
1013
+ const filter = (item, q) => {
1014
+ if (!q) return true;
1015
+ const lowerQ = q.toLowerCase();
1016
+ return item.label.toLowerCase().includes(lowerQ) || String(item.value).toLowerCase().includes(lowerQ);
1017
+ };
1018
+ const getFiltered = () => {
1019
+ return items.filter((item) => filter(item, query));
1020
+ };
1021
+ const clearRender = () => {
1022
+ if (lastRenderHeight > 0) {
1023
+ process.stdout.write(`\x1b[${lastRenderHeight}A`);
1024
+ for (let i = 0; i < lastRenderHeight; i++) process.stdout.write("\x1B[2K\x1B[1B");
1025
+ process.stdout.write(`\x1b[${lastRenderHeight}A`);
1026
+ }
1027
+ };
1028
+ const render = (state = "active") => {
1029
+ clearRender();
1030
+ const lines = [];
1031
+ const filtered = getFiltered();
1032
+ const icon = state === "active" ? S_STEP_ACTIVE : state === "cancel" ? S_STEP_CANCEL : S_STEP_SUBMIT;
1033
+ lines.push(`${icon} ${import_picocolors.default.bold(message)}`);
1034
+ if (state === "active") {
1035
+ if (lockedSection && lockedSection.items.length > 0) {
1036
+ lines.push(`${S_BAR}`);
1037
+ lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${import_picocolors.default.bold(lockedSection.title)} ${S_BAR_H.repeat(30)}`);
1038
+ for (const item of lockedSection.items) lines.push(`${S_BAR} ${S_CHECKBOX_LOCKED} ${item.label}`);
1039
+ lines.push(`${S_BAR}`);
1040
+ lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${import_picocolors.default.bold("Other agents")} ${S_BAR_H.repeat(34)}`);
1041
+ }
1042
+ const searchLine = `${S_BAR} ${import_picocolors.default.dim("Search:")} ${query}${import_picocolors.default.inverse(" ")}`;
1043
+ lines.push(searchLine);
1044
+ lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, space select, enter confirm")}`);
1045
+ lines.push(`${S_BAR}`);
1046
+ const visibleStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), filtered.length - maxVisible));
1047
+ const visibleEnd = Math.min(filtered.length, visibleStart + maxVisible);
1048
+ const visibleItems = filtered.slice(visibleStart, visibleEnd);
1049
+ if (filtered.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("No matches found")}`);
1050
+ else {
1051
+ for (let i = 0; i < visibleItems.length; i++) {
1052
+ const item = visibleItems[i];
1053
+ const actualIndex = visibleStart + i;
1054
+ const isSelected = selected.has(item.value);
1055
+ const isCursor = actualIndex === cursor;
1056
+ const radio = isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE;
1057
+ const label = isCursor ? import_picocolors.default.underline(item.label) : item.label;
1058
+ const hint = item.hint ? import_picocolors.default.dim(` (${item.hint})`) : "";
1059
+ const prefix = isCursor ? import_picocolors.default.cyan("❯") : " ";
1060
+ lines.push(`${S_BAR} ${prefix} ${radio} ${label}${hint}`);
1061
+ }
1062
+ const hiddenBefore = visibleStart;
1063
+ const hiddenAfter = filtered.length - visibleEnd;
1064
+ if (hiddenBefore > 0 || hiddenAfter > 0) {
1065
+ const parts = [];
1066
+ if (hiddenBefore > 0) parts.push(`↑ ${hiddenBefore} more`);
1067
+ if (hiddenAfter > 0) parts.push(`↓ ${hiddenAfter} more`);
1068
+ lines.push(`${S_BAR} ${import_picocolors.default.dim(parts.join(" "))}`);
1069
+ }
1070
+ }
1071
+ lines.push(`${S_BAR}`);
1072
+ const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
1073
+ if (allSelectedLabels.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("Selected: (none)")}`);
1074
+ else {
1075
+ const summary = allSelectedLabels.length <= 3 ? allSelectedLabels.join(", ") : `${allSelectedLabels.slice(0, 3).join(", ")} +${allSelectedLabels.length - 3} more`;
1076
+ lines.push(`${S_BAR} ${import_picocolors.default.green("Selected:")} ${summary}`);
1077
+ }
1078
+ lines.push(`${import_picocolors.default.dim("└")}`);
1079
+ } else if (state === "submit") {
1080
+ const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
1081
+ lines.push(`${S_BAR} ${import_picocolors.default.dim(allSelectedLabels.join(", "))}`);
1082
+ } else if (state === "cancel") lines.push(`${S_BAR} ${import_picocolors.default.strikethrough(import_picocolors.default.dim("Cancelled"))}`);
1083
+ process.stdout.write(lines.join("\n") + "\n");
1084
+ lastRenderHeight = lines.length;
1085
+ };
1086
+ const cleanup = () => {
1087
+ process.stdin.removeListener("keypress", keypressHandler);
1088
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
1089
+ rl.close();
1090
+ };
1091
+ const submit = () => {
1092
+ if (required && selected.size === 0 && lockedValues.length === 0) return;
1093
+ render("submit");
1094
+ cleanup();
1095
+ resolve([...lockedValues, ...Array.from(selected)]);
1096
+ };
1097
+ const cancel = () => {
1098
+ render("cancel");
1099
+ cleanup();
1100
+ resolve(cancelSymbol);
1101
+ };
1102
+ const keypressHandler = (_str, key) => {
1103
+ if (!key) return;
1104
+ const filtered = getFiltered();
1105
+ if (key.name === "return") {
1106
+ submit();
1107
+ return;
1108
+ }
1109
+ if (key.name === "escape" || key.ctrl && key.name === "c") {
1110
+ cancel();
1111
+ return;
1112
+ }
1113
+ if (key.name === "up") {
1114
+ cursor = Math.max(0, cursor - 1);
1115
+ render();
1116
+ return;
1117
+ }
1118
+ if (key.name === "down") {
1119
+ cursor = Math.min(filtered.length - 1, cursor + 1);
1120
+ render();
1121
+ return;
1122
+ }
1123
+ if (key.name === "space") {
1124
+ const item = filtered[cursor];
1125
+ if (item) if (selected.has(item.value)) selected.delete(item.value);
1126
+ else selected.add(item.value);
1127
+ render();
1128
+ return;
1129
+ }
1130
+ if (key.name === "backspace") {
1131
+ query = query.slice(0, -1);
1132
+ cursor = 0;
1133
+ render();
1134
+ return;
1135
+ }
1136
+ if (key.sequence && !key.ctrl && !key.meta && key.sequence.length === 1) {
1137
+ query += key.sequence;
1138
+ cursor = 0;
1139
+ render();
1140
+ return;
1141
+ }
1142
+ };
1143
+ process.stdin.on("keypress", keypressHandler);
1144
+ render();
1145
+ });
1146
+ }
1147
+ const isCancelled = (value) => typeof value === "symbol";
1148
+ function multiselect(opts) {
1149
+ return fe({
1150
+ ...opts,
1151
+ options: opts.options,
1152
+ message: `${opts.message} ${import_picocolors.default.dim("(space to toggle)")}`
1153
+ });
1154
+ }
1155
+ async function promptForAgents(message, choices) {
1156
+ let lastSelected;
1157
+ try {
1158
+ lastSelected = await getLastSelectedAgents();
1159
+ } catch {}
1160
+ const validAgents = choices.map((c) => c.value);
1161
+ const defaultValues = [
1162
+ "claude-code",
1163
+ "opencode",
1164
+ "codex"
1165
+ ].filter((a) => validAgents.includes(a));
1166
+ let initialValues = [];
1167
+ if (lastSelected && lastSelected.length > 0) initialValues = lastSelected.filter((a) => validAgents.includes(a));
1168
+ if (initialValues.length === 0) initialValues = defaultValues;
1169
+ const selected = await searchMultiselect({
1170
+ message,
1171
+ items: choices,
1172
+ initialSelected: initialValues,
1173
+ required: true
1174
+ });
1175
+ if (!isCancelled(selected)) try {
1176
+ await saveSelectedAgents(selected);
1177
+ } catch {}
1178
+ return selected;
1179
+ }
1180
+ async function selectAgentsInteractive(options) {
1181
+ const supportsGlobalFilter = (a) => !options.global || agents[a].globalSkillsDir;
1182
+ const universalAgents = getUniversalAgents().filter(supportsGlobalFilter);
1183
+ const otherAgents = getNonUniversalAgents().filter(supportsGlobalFilter);
1184
+ const universalSection = {
1185
+ title: "Universal (.agents/skills)",
1186
+ items: universalAgents.map((a) => ({
1187
+ value: a,
1188
+ label: agents[a].displayName
1189
+ }))
1190
+ };
1191
+ const otherChoices = otherAgents.map((a) => ({
1192
+ value: a,
1193
+ label: agents[a].displayName,
1194
+ hint: options.global ? agents[a].globalSkillsDir : agents[a].skillsDir
1195
+ }));
1196
+ let lastSelected;
1197
+ try {
1198
+ lastSelected = await getLastSelectedAgents();
1199
+ } catch {}
1200
+ const selected = await searchMultiselect({
1201
+ message: "Which agents do you want to install to?",
1202
+ items: otherChoices,
1203
+ initialSelected: lastSelected ? lastSelected.filter((a) => otherAgents.includes(a) && !universalAgents.includes(a)) : [],
1204
+ lockedSection: universalSection
1205
+ });
1206
+ if (!isCancelled(selected)) try {
1207
+ await saveSelectedAgents(selected);
1208
+ } catch {}
1209
+ return selected;
1210
+ }
1211
+ const CLONE_TIMEOUT_MS = 6e4;
1212
+ var GitCloneError = class extends Error {
1213
+ url;
1214
+ isTimeout;
1215
+ isAuthError;
1216
+ constructor(message, url, isTimeout = false, isAuthError = false) {
1217
+ super(message);
1218
+ this.name = "GitCloneError";
1219
+ this.url = url;
1220
+ this.isTimeout = isTimeout;
1221
+ this.isAuthError = isAuthError;
1222
+ }
1223
+ };
1224
+ async function cloneRepo(url, ref) {
1225
+ const tempDir = await mkdtemp(join(tmpdir(), "skills-"));
1226
+ const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
1227
+ const cloneOptions = ref ? [
1228
+ "--depth",
1229
+ "1",
1230
+ "--branch",
1231
+ ref
1232
+ ] : ["--depth", "1"];
1233
+ try {
1234
+ await git.clone(url, tempDir, cloneOptions);
1235
+ return tempDir;
1236
+ } catch (error) {
1237
+ await rm(tempDir, {
1238
+ recursive: true,
1239
+ force: true
1240
+ }).catch(() => {});
1241
+ const errorMessage = error instanceof Error ? error.message : String(error);
1242
+ const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
1243
+ const isAuthError = errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("Permission denied") || errorMessage.includes("Repository not found");
1244
+ if (isTimeout) throw new GitCloneError("Clone timed out after 60s. This often happens with private repos that require authentication.\n Ensure you have access and your SSH keys or credentials are configured:\n - For SSH: ssh-add -l (to check loaded keys)\n - For HTTPS: gh auth status (if using GitHub CLI)", url, true, false);
1245
+ if (isAuthError) throw new GitCloneError(`Authentication failed for ${url}.\n - For private repos, ensure you have access\n - For SSH: Check your keys with 'ssh -T git@github.com'\n - For HTTPS: Run 'gh auth login' or configure git credentials`, url, false, true);
1246
+ throw new GitCloneError(`Failed to clone ${url}: ${errorMessage}`, url, false, false);
1247
+ }
1248
+ }
1249
+ async function cleanupTempDir(dir) {
1250
+ const normalizedDir = normalize(resolve(dir));
1251
+ const normalizedTmpDir = normalize(resolve(tmpdir()));
1252
+ if (!normalizedDir.startsWith(normalizedTmpDir + sep) && normalizedDir !== normalizedTmpDir) throw new Error("Attempted to clean up directory outside of temp directory");
1253
+ await rm(dir, {
1254
+ recursive: true,
1255
+ force: true
1256
+ });
1257
+ }
1258
+ var import_gray_matter = /* @__PURE__ */ __toESM(require_gray_matter(), 1);
1259
+ function shouldInstallInternalSkills() {
1260
+ const envValue = process.env.INSTALL_INTERNAL_SKILLS;
1261
+ return envValue === "1" || envValue === "true";
1262
+ }
1263
+ async function hasCognitiveMd(dir, cognitiveType) {
1264
+ try {
1265
+ const fileName = COGNITIVE_FILE_NAMES[cognitiveType];
1266
+ return (await stat(join(dir, fileName))).isFile();
1267
+ } catch {
1268
+ return false;
1269
+ }
1270
+ }
1271
+ async function parseCognitiveMd(mdPath, cognitiveType, options) {
1272
+ try {
1273
+ const content = await readFile(mdPath, "utf-8");
1274
+ const { data } = (0, import_gray_matter.default)(content);
1275
+ if (!data.name || !data.description) return null;
1276
+ if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
1277
+ return {
1278
+ name: data.name,
1279
+ description: data.description,
1280
+ path: dirname(mdPath),
1281
+ rawContent: content,
1282
+ metadata: data.metadata,
1283
+ cognitiveType
1284
+ };
1285
+ } catch {
1286
+ return null;
1287
+ }
1288
+ }
1289
+ async function parseSkillMd(skillMdPath, options) {
1290
+ return parseCognitiveMd(skillMdPath, "skill", options);
1291
+ }
1292
+ function isContainedIn(targetPath, basePath) {
1293
+ const normalizedBase = normalize(resolve(basePath));
1294
+ const normalizedTarget = normalize(resolve(targetPath));
1295
+ return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
1296
+ }
1297
+ function isValidRelativePath(path) {
1298
+ return path.startsWith("./");
1299
+ }
1300
+ async function getPluginSkillPaths(basePath) {
1301
+ const searchDirs = [];
1302
+ const addPluginSkillPaths = (pluginBase, skills) => {
1303
+ if (!isContainedIn(pluginBase, basePath)) return;
1304
+ if (skills && skills.length > 0) for (const skillPath of skills) {
1305
+ if (!isValidRelativePath(skillPath)) continue;
1306
+ const skillDir = dirname(join(pluginBase, skillPath));
1307
+ if (isContainedIn(skillDir, basePath)) searchDirs.push(skillDir);
1308
+ }
1309
+ searchDirs.push(join(pluginBase, "skills"));
1310
+ };
1311
+ try {
1312
+ const content = await readFile(join(basePath, ".claude-plugin/marketplace.json"), "utf-8");
1313
+ const manifest = JSON.parse(content);
1314
+ const pluginRoot = manifest.metadata?.pluginRoot;
1315
+ if (pluginRoot === void 0 || isValidRelativePath(pluginRoot)) for (const plugin of manifest.plugins ?? []) {
1316
+ if (typeof plugin.source !== "string" && plugin.source !== void 0) continue;
1317
+ if (plugin.source !== void 0 && !isValidRelativePath(plugin.source)) continue;
1318
+ addPluginSkillPaths(join(basePath, pluginRoot ?? "", plugin.source ?? ""), plugin.skills);
1319
+ }
1320
+ } catch {}
1321
+ try {
1322
+ const content = await readFile(join(basePath, ".claude-plugin/plugin.json"), "utf-8");
1323
+ addPluginSkillPaths(basePath, JSON.parse(content).skills);
1324
+ } catch {}
1325
+ return searchDirs;
1326
+ }
1327
+ const SKIP_DIRS = [
1328
+ "node_modules",
1329
+ ".git",
1330
+ "dist",
1331
+ "build",
1332
+ "__pycache__"
1333
+ ];
1334
+ const ALL_COGNITIVE_TYPES = [
1335
+ "skill",
1336
+ "agent",
1337
+ "prompt"
1338
+ ];
1339
+ const AGENT_DIR_PREFIXES = [
1340
+ ".agent",
1341
+ ".agents",
1342
+ ".claude",
1343
+ ".cline",
1344
+ ".codebuddy",
1345
+ ".codex",
1346
+ ".commandcode",
1347
+ ".continue",
1348
+ ".cursor",
1349
+ ".github",
1350
+ ".goose",
1351
+ ".iflow",
1352
+ ".junie",
1353
+ ".kilocode",
1354
+ ".kiro",
1355
+ ".mux",
1356
+ ".neovate",
1357
+ ".opencode",
1358
+ ".openhands",
1359
+ ".pi",
1360
+ ".qoder",
1361
+ ".roo",
1362
+ ".trae",
1363
+ ".windsurf",
1364
+ ".zencoder"
1365
+ ];
1366
+ async function findCognitiveDirs(dir, types = ALL_COGNITIVE_TYPES, depth = 0, maxDepth = 5) {
1367
+ if (depth > maxDepth) return [];
1368
+ try {
1369
+ const checks = types.map(async (ct) => ({
1370
+ type: ct,
1371
+ found: await hasCognitiveMd(dir, ct)
1372
+ }));
1373
+ const [results, entries] = await Promise.all([Promise.all(checks), readdir(dir, { withFileTypes: true }).catch(() => [])]);
1374
+ const currentDir = results.filter((r) => r.found).map((r) => ({
1375
+ dir,
1376
+ cognitiveType: r.type
1377
+ }));
1378
+ const subDirResults = await Promise.all(entries.filter((entry) => entry.isDirectory() && !SKIP_DIRS.includes(entry.name)).map((entry) => findCognitiveDirs(join(dir, entry.name), types, depth + 1, maxDepth)));
1379
+ return [...currentDir, ...subDirResults.flat()];
1380
+ } catch {
1381
+ return [];
1382
+ }
1383
+ }
1384
+ function buildPrioritySearchDirs(searchPath, cognitiveType) {
1385
+ const subdir = COGNITIVE_SUBDIRS[cognitiveType];
1386
+ const dirs = [
1387
+ searchPath,
1388
+ join(searchPath, subdir),
1389
+ join(searchPath, `${subdir}/.curated`),
1390
+ join(searchPath, `${subdir}/.experimental`),
1391
+ join(searchPath, `${subdir}/.system`)
1392
+ ];
1393
+ for (const prefix of AGENT_DIR_PREFIXES) dirs.push(join(searchPath, prefix, subdir));
1394
+ return dirs;
1395
+ }
1396
+ async function discoverCognitives(basePath, subpath, options) {
1397
+ const types = options?.types ?? ["skill"];
1398
+ const results = [];
1399
+ const seenKeys = /* @__PURE__ */ new Set();
1400
+ const searchPath = subpath ? join(basePath, subpath) : basePath;
1401
+ const makeKey = (cognitiveType, name) => `${cognitiveType}::${name}`;
1402
+ const addResult = (skill) => {
1403
+ const key = makeKey(skill.cognitiveType ?? "skill", skill.name);
1404
+ if (seenKeys.has(key)) return false;
1405
+ seenKeys.add(key);
1406
+ results.push(skill);
1407
+ return true;
1408
+ };
1409
+ for (const ct of types) if (await hasCognitiveMd(searchPath, ct)) {
1410
+ const fileName = COGNITIVE_FILE_NAMES[ct];
1411
+ const cognitive = await parseCognitiveMd(join(searchPath, fileName), ct, options);
1412
+ if (cognitive) addResult(cognitive);
1413
+ }
1414
+ if (results.length > 0 && !options?.fullDepth) return results;
1415
+ const allPriorityDirs = /* @__PURE__ */ new Set();
1416
+ const dirTypeMap = /* @__PURE__ */ new Map();
1417
+ for (const ct of types) {
1418
+ const dirs = buildPrioritySearchDirs(searchPath, ct);
1419
+ if (ct === "skill") dirs.push(...await getPluginSkillPaths(searchPath));
1420
+ for (const dir of dirs) {
1421
+ allPriorityDirs.add(dir);
1422
+ if (!dirTypeMap.has(dir)) dirTypeMap.set(dir, /* @__PURE__ */ new Set());
1423
+ dirTypeMap.get(dir).add(ct);
1424
+ }
1425
+ }
1426
+ for (const dir of allPriorityDirs) {
1427
+ const typesForDir = dirTypeMap.get(dir);
1428
+ try {
1429
+ const entries = await readdir(dir, { withFileTypes: true });
1430
+ for (const entry of entries) if (entry.isDirectory()) {
1431
+ const subDir = join(dir, entry.name);
1432
+ for (const ct of typesForDir) if (await hasCognitiveMd(subDir, ct)) {
1433
+ const fileName = COGNITIVE_FILE_NAMES[ct];
1434
+ const cognitive = await parseCognitiveMd(join(subDir, fileName), ct, options);
1435
+ if (cognitive) addResult(cognitive);
1436
+ }
1437
+ }
1438
+ } catch {}
1439
+ }
1440
+ if (results.length === 0 || options?.fullDepth) {
1441
+ const allCognitiveDirs = await findCognitiveDirs(searchPath, types);
1442
+ for (const { dir: cogDir, cognitiveType: ct } of allCognitiveDirs) {
1443
+ const fileName = COGNITIVE_FILE_NAMES[ct];
1444
+ const cognitive = await parseCognitiveMd(join(cogDir, fileName), ct, options);
1445
+ if (cognitive) addResult(cognitive);
1446
+ }
1447
+ }
1448
+ return results;
1449
+ }
1450
+ async function discoverSkills(basePath, subpath, options) {
1451
+ return discoverCognitives(basePath, subpath, {
1452
+ ...options,
1453
+ types: options?.types ?? ["skill"]
1454
+ });
1455
+ }
1456
+ function getSkillDisplayName(skill) {
1457
+ return skill.name || basename(skill.path);
1458
+ }
1459
+ function filterSkills(skills, inputNames) {
1460
+ const normalizedInputs = inputNames.map((n) => n.toLowerCase());
1461
+ return skills.filter((skill) => {
1462
+ const name = skill.name.toLowerCase();
1463
+ const displayName = getSkillDisplayName(skill).toLowerCase();
1464
+ return normalizedInputs.some((input) => input === name || input === displayName);
1465
+ });
1466
+ }
1467
+ function sanitizeName(name) {
1468
+ return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").substring(0, 255) || "unnamed-skill";
1469
+ }
1470
+ function isPathSafe(basePath, targetPath) {
1471
+ const normalizedBase = normalize(resolve(basePath));
1472
+ const normalizedTarget = normalize(resolve(targetPath));
1473
+ return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
1474
+ }
1475
+ function getCanonicalDir(cognitiveType, global, cwd) {
1476
+ return join(global ? homedir() : cwd || process.cwd(), AGENTS_DIR$3, COGNITIVE_SUBDIRS[cognitiveType]);
1477
+ }
1478
+ function getInstallPath(skillName, agentType, options = {}) {
1479
+ const cognitiveType = options.cognitiveType ?? "skill";
1480
+ const cwd = options.cwd || process.cwd();
1481
+ const sanitized = sanitizeName(skillName);
1482
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1483
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1484
+ const targetBase = options.global && globalDir !== void 0 ? globalDir : join(cwd, localDir);
1485
+ const installPath = join(targetBase, sanitized);
1486
+ if (!isPathSafe(targetBase, installPath)) throw new Error(`Invalid ${cognitiveType} name: potential path traversal detected`);
1487
+ return installPath;
1488
+ }
1489
+ function getCanonicalPath(skillName, options = {}) {
1490
+ const cognitiveType = options.cognitiveType ?? "skill";
1491
+ const sanitized = sanitizeName(skillName);
1492
+ const canonicalBase = getCanonicalDir(cognitiveType, options.global ?? false, options.cwd);
1493
+ const canonicalPath = join(canonicalBase, sanitized);
1494
+ if (!isPathSafe(canonicalBase, canonicalPath)) throw new Error(`Invalid ${cognitiveType} name: potential path traversal detected`);
1495
+ return canonicalPath;
1496
+ }
1497
+ const EXCLUDE_FILES = new Set(["README.md", "metadata.json"]);
1498
+ const EXCLUDE_DIRS = new Set([".git"]);
1499
+ const isExcluded = (name, isDirectory = false) => {
1500
+ if (EXCLUDE_FILES.has(name)) return true;
1501
+ if (name.startsWith("_")) return true;
1502
+ if (isDirectory && EXCLUDE_DIRS.has(name)) return true;
1503
+ return false;
1504
+ };
1505
+ async function copyDirectory(src, dest) {
1506
+ await mkdir(dest, { recursive: true });
1507
+ const entries = await readdir(src, { withFileTypes: true });
1508
+ await Promise.all(entries.filter((entry) => !isExcluded(entry.name, entry.isDirectory())).map(async (entry) => {
1509
+ const srcPath = join(src, entry.name);
1510
+ const destPath = join(dest, entry.name);
1511
+ if (entry.isDirectory()) await copyDirectory(srcPath, destPath);
1512
+ else await cp(srcPath, destPath, {
1513
+ dereference: true,
1514
+ recursive: true
1515
+ });
1516
+ }));
1517
+ }
1518
+ async function cleanAndCreateDirectory(path) {
1519
+ try {
1520
+ await rm(path, {
1521
+ recursive: true,
1522
+ force: true
1523
+ });
1524
+ } catch {}
1525
+ await mkdir(path, { recursive: true });
1526
+ }
1527
+ async function resolveParentSymlinks(path) {
1528
+ const resolved = resolve(path);
1529
+ const dir = dirname(resolved);
1530
+ const base = basename(resolved);
1531
+ try {
1532
+ const { realpath } = await import("fs/promises");
1533
+ return join(await realpath(dir), base);
1534
+ } catch {
1535
+ return resolved;
1536
+ }
1537
+ }
1538
+ function resolveSymlinkTarget(linkPath, linkTarget) {
1539
+ return resolve(dirname(linkPath), linkTarget);
1540
+ }
1541
+ async function createSymlink(target, linkPath) {
1542
+ try {
1543
+ const resolvedTarget = resolve(target);
1544
+ if (resolvedTarget === resolve(linkPath)) return true;
1545
+ if (await resolveParentSymlinks(target) === await resolveParentSymlinks(linkPath)) return true;
1546
+ try {
1547
+ if ((await lstat(linkPath)).isSymbolicLink()) {
1548
+ if (resolveSymlinkTarget(linkPath, await readlink(linkPath)) === resolvedTarget) return true;
1549
+ await rm(linkPath);
1550
+ } else await rm(linkPath, { recursive: true });
1551
+ } catch (err) {
1552
+ if (err && typeof err === "object" && "code" in err && err.code === "ELOOP") try {
1553
+ await rm(linkPath, { force: true });
1554
+ } catch {}
1555
+ }
1556
+ const linkDir = dirname(linkPath);
1557
+ await mkdir(linkDir, { recursive: true });
1558
+ await symlink(relative(await resolveParentSymlinks(linkDir), target), linkPath, platform() === "win32" ? "junction" : void 0);
1559
+ return true;
1560
+ } catch {
1561
+ return false;
1562
+ }
1563
+ }
1564
+ async function installCognitiveForAgent(cognitive, agentType, options = {}) {
1565
+ const cognitiveType = options.cognitiveType ?? "skill";
1566
+ const agent = agents[agentType];
1567
+ const isGlobal = options.global ?? false;
1568
+ const cwd = options.cwd || process.cwd();
1569
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1570
+ if (isGlobal && globalDir === void 0) return {
1571
+ success: false,
1572
+ path: "",
1573
+ mode: options.mode ?? "symlink",
1574
+ error: `${agent.displayName} does not support global ${cognitiveType} installation`
1575
+ };
1576
+ const safeName = sanitizeName(cognitive.name || basename(cognitive.path));
1577
+ const canonicalBase = getCanonicalDir(cognitiveType, isGlobal, cwd);
1578
+ const canonicalDir = join(canonicalBase, safeName);
1579
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1580
+ const agentBase = isGlobal ? globalDir : join(cwd, localDir);
1581
+ const agentDir = join(agentBase, safeName);
1582
+ const installMode = options.mode ?? "symlink";
1583
+ if (!isPathSafe(canonicalBase, canonicalDir)) return {
1584
+ success: false,
1585
+ path: agentDir,
1586
+ mode: installMode,
1587
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1588
+ };
1589
+ if (!isPathSafe(agentBase, agentDir)) return {
1590
+ success: false,
1591
+ path: agentDir,
1592
+ mode: installMode,
1593
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1594
+ };
1595
+ try {
1596
+ if (installMode === "copy") {
1597
+ await cleanAndCreateDirectory(agentDir);
1598
+ await copyDirectory(cognitive.path, agentDir);
1599
+ return {
1600
+ success: true,
1601
+ path: agentDir,
1602
+ mode: "copy"
1603
+ };
1604
+ }
1605
+ await cleanAndCreateDirectory(canonicalDir);
1606
+ await copyDirectory(cognitive.path, canonicalDir);
1607
+ if (isGlobal && isUniversalForType(agentType, cognitiveType)) return {
1608
+ success: true,
1609
+ path: canonicalDir,
1610
+ canonicalPath: canonicalDir,
1611
+ mode: "symlink"
1612
+ };
1613
+ if (!await createSymlink(canonicalDir, agentDir)) {
1614
+ await cleanAndCreateDirectory(agentDir);
1615
+ await copyDirectory(cognitive.path, agentDir);
1616
+ return {
1617
+ success: true,
1618
+ path: agentDir,
1619
+ canonicalPath: canonicalDir,
1620
+ mode: "symlink",
1621
+ symlinkFailed: true
1622
+ };
1623
+ }
1624
+ return {
1625
+ success: true,
1626
+ path: agentDir,
1627
+ canonicalPath: canonicalDir,
1628
+ mode: "symlink"
1629
+ };
1630
+ } catch (error) {
1631
+ return {
1632
+ success: false,
1633
+ path: agentDir,
1634
+ mode: installMode,
1635
+ error: error instanceof Error ? error.message : "Unknown error"
1636
+ };
1637
+ }
1638
+ }
1639
+ async function installRemoteSkillForAgent(skill, agentType, options = {}) {
1640
+ const cognitiveType = options.cognitiveType ?? skill.cognitiveType ?? "skill";
1641
+ const agent = agents[agentType];
1642
+ const isGlobal = options.global ?? false;
1643
+ const cwd = options.cwd || process.cwd();
1644
+ const installMode = options.mode ?? "symlink";
1645
+ const fileName = COGNITIVE_FILE_NAMES[cognitiveType];
1646
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1647
+ if (isGlobal && globalDir === void 0) return {
1648
+ success: false,
1649
+ path: "",
1650
+ mode: installMode,
1651
+ error: `${agent.displayName} does not support global ${cognitiveType} installation`
1652
+ };
1653
+ const skillName = sanitizeName(skill.installName);
1654
+ const canonicalBase = getCanonicalDir(cognitiveType, isGlobal, cwd);
1655
+ const canonicalDir = join(canonicalBase, skillName);
1656
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1657
+ const agentBase = isGlobal ? globalDir : join(cwd, localDir);
1658
+ const agentDir = join(agentBase, skillName);
1659
+ if (!isPathSafe(canonicalBase, canonicalDir)) return {
1660
+ success: false,
1661
+ path: agentDir,
1662
+ mode: installMode,
1663
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1664
+ };
1665
+ if (!isPathSafe(agentBase, agentDir)) return {
1666
+ success: false,
1667
+ path: agentDir,
1668
+ mode: installMode,
1669
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1670
+ };
1671
+ try {
1672
+ if (installMode === "copy") {
1673
+ await cleanAndCreateDirectory(agentDir);
1674
+ await writeFile(join(agentDir, fileName), skill.content, "utf-8");
1675
+ return {
1676
+ success: true,
1677
+ path: agentDir,
1678
+ mode: "copy"
1679
+ };
1680
+ }
1681
+ await cleanAndCreateDirectory(canonicalDir);
1682
+ await writeFile(join(canonicalDir, fileName), skill.content, "utf-8");
1683
+ if (isGlobal && isUniversalForType(agentType, cognitiveType)) return {
1684
+ success: true,
1685
+ path: canonicalDir,
1686
+ canonicalPath: canonicalDir,
1687
+ mode: "symlink"
1688
+ };
1689
+ if (!await createSymlink(canonicalDir, agentDir)) {
1690
+ await cleanAndCreateDirectory(agentDir);
1691
+ await writeFile(join(agentDir, fileName), skill.content, "utf-8");
1692
+ return {
1693
+ success: true,
1694
+ path: agentDir,
1695
+ canonicalPath: canonicalDir,
1696
+ mode: "symlink",
1697
+ symlinkFailed: true
1698
+ };
1699
+ }
1700
+ return {
1701
+ success: true,
1702
+ path: agentDir,
1703
+ canonicalPath: canonicalDir,
1704
+ mode: "symlink"
1705
+ };
1706
+ } catch (error) {
1707
+ return {
1708
+ success: false,
1709
+ path: agentDir,
1710
+ mode: installMode,
1711
+ error: error instanceof Error ? error.message : "Unknown error"
1712
+ };
1713
+ }
1714
+ }
1715
+ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
1716
+ const cognitiveType = options.cognitiveType ?? "skill";
1717
+ const agent = agents[agentType];
1718
+ const isGlobal = options.global ?? false;
1719
+ const cwd = options.cwd || process.cwd();
1720
+ const installMode = options.mode ?? "symlink";
1721
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1722
+ if (isGlobal && globalDir === void 0) return {
1723
+ success: false,
1724
+ path: "",
1725
+ mode: installMode,
1726
+ error: `${agent.displayName} does not support global ${cognitiveType} installation`
1727
+ };
1728
+ const skillName = sanitizeName(skill.installName);
1729
+ const canonicalBase = getCanonicalDir(cognitiveType, isGlobal, cwd);
1730
+ const canonicalDir = join(canonicalBase, skillName);
1731
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1732
+ const agentBase = isGlobal ? globalDir : join(cwd, localDir);
1733
+ const agentDir = join(agentBase, skillName);
1734
+ if (!isPathSafe(canonicalBase, canonicalDir)) return {
1735
+ success: false,
1736
+ path: agentDir,
1737
+ mode: installMode,
1738
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1739
+ };
1740
+ if (!isPathSafe(agentBase, agentDir)) return {
1741
+ success: false,
1742
+ path: agentDir,
1743
+ mode: installMode,
1744
+ error: `Invalid ${cognitiveType} name: potential path traversal detected`
1745
+ };
1746
+ async function writeSkillFiles(targetDir) {
1747
+ for (const [filePath, content] of skill.files) {
1748
+ const fullPath = join(targetDir, filePath);
1749
+ if (!isPathSafe(targetDir, fullPath)) continue;
1750
+ const parentDir = dirname(fullPath);
1751
+ if (parentDir !== targetDir) await mkdir(parentDir, { recursive: true });
1752
+ await writeFile(fullPath, content, "utf-8");
1753
+ }
1754
+ }
1755
+ try {
1756
+ if (installMode === "copy") {
1757
+ await cleanAndCreateDirectory(agentDir);
1758
+ await writeSkillFiles(agentDir);
1759
+ return {
1760
+ success: true,
1761
+ path: agentDir,
1762
+ mode: "copy"
1763
+ };
1764
+ }
1765
+ await cleanAndCreateDirectory(canonicalDir);
1766
+ await writeSkillFiles(canonicalDir);
1767
+ if (isGlobal && isUniversalForType(agentType, cognitiveType)) return {
1768
+ success: true,
1769
+ path: canonicalDir,
1770
+ canonicalPath: canonicalDir,
1771
+ mode: "symlink"
1772
+ };
1773
+ if (!await createSymlink(canonicalDir, agentDir)) {
1774
+ await cleanAndCreateDirectory(agentDir);
1775
+ await writeSkillFiles(agentDir);
1776
+ return {
1777
+ success: true,
1778
+ path: agentDir,
1779
+ canonicalPath: canonicalDir,
1780
+ mode: "symlink",
1781
+ symlinkFailed: true
1782
+ };
1783
+ }
1784
+ return {
1785
+ success: true,
1786
+ path: agentDir,
1787
+ canonicalPath: canonicalDir,
1788
+ mode: "symlink"
1789
+ };
1790
+ } catch (error) {
1791
+ return {
1792
+ success: false,
1793
+ path: agentDir,
1794
+ mode: installMode,
1795
+ error: error instanceof Error ? error.message : "Unknown error"
1796
+ };
1797
+ }
1798
+ }
1799
+ async function listInstalledCognitives(options = {}) {
1800
+ const cwd = options.cwd || process.cwd();
1801
+ const typesToScan = options.typeFilter ?? [
1802
+ "skill",
1803
+ "agent",
1804
+ "prompt"
1805
+ ];
1806
+ const cognitivesMap = /* @__PURE__ */ new Map();
1807
+ const detectedAgents = await detectInstalledAgents();
1808
+ const agentFilter = options.agentFilter;
1809
+ const agentsToCheck = agentFilter ? detectedAgents.filter((a) => agentFilter.includes(a)) : detectedAgents;
1810
+ const scopeTypes = [];
1811
+ if (options.global === void 0) scopeTypes.push({ global: false }, { global: true });
1812
+ else scopeTypes.push({ global: options.global });
1813
+ for (const cognitiveType of typesToScan) {
1814
+ const fileName = COGNITIVE_FILE_NAMES[cognitiveType];
1815
+ const scopes = [];
1816
+ for (const { global: isGlobal } of scopeTypes) {
1817
+ scopes.push({
1818
+ global: isGlobal,
1819
+ path: getCanonicalDir(cognitiveType, isGlobal, cwd)
1820
+ });
1821
+ for (const agentType of agentsToCheck) {
1822
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1823
+ if (isGlobal && globalDir === void 0) continue;
1824
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1825
+ const agentDir = isGlobal ? globalDir : join(cwd, localDir);
1826
+ if (!scopes.some((s) => s.path === agentDir && s.global === isGlobal)) scopes.push({
1827
+ global: isGlobal,
1828
+ path: agentDir,
1829
+ agentType
1830
+ });
1831
+ }
1832
+ }
1833
+ for (const scope of scopes) try {
1834
+ const entries = await readdir(scope.path, { withFileTypes: true });
1835
+ for (const entry of entries) {
1836
+ if (!entry.isDirectory()) continue;
1837
+ const cognitiveDir = join(scope.path, entry.name);
1838
+ const mdPath = join(cognitiveDir, fileName);
1839
+ try {
1840
+ await stat(mdPath);
1841
+ } catch {
1842
+ continue;
1843
+ }
1844
+ const parsed = cognitiveType === "skill" ? await parseSkillMd(mdPath) : await parseCognitiveMd(mdPath, cognitiveType);
1845
+ if (!parsed) continue;
1846
+ const scopeKey = scope.global ? "global" : "project";
1847
+ const cognitiveKey = `${scopeKey}:${cognitiveType}:${parsed.name}`;
1848
+ if (scope.agentType) {
1849
+ if (cognitivesMap.has(cognitiveKey)) {
1850
+ const existing = cognitivesMap.get(cognitiveKey);
1851
+ if (!existing.agents.includes(scope.agentType)) existing.agents.push(scope.agentType);
1852
+ } else cognitivesMap.set(cognitiveKey, {
1853
+ name: parsed.name,
1854
+ description: parsed.description,
1855
+ path: cognitiveDir,
1856
+ canonicalPath: cognitiveDir,
1857
+ scope: scopeKey,
1858
+ agents: [scope.agentType],
1859
+ cognitiveType
1860
+ });
1861
+ continue;
1862
+ }
1863
+ const sanitizedName = sanitizeName(parsed.name);
1864
+ const installedAgents = [];
1865
+ for (const agentType of agentsToCheck) {
1866
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1867
+ if (scope.global && globalDir === void 0) continue;
1868
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1869
+ const agentBase = scope.global ? globalDir : join(cwd, localDir);
1870
+ let found = false;
1871
+ const possibleNames = Array.from(new Set([
1872
+ entry.name,
1873
+ sanitizedName,
1874
+ parsed.name.toLowerCase().replace(/\s+/g, "-").replace(/[\/\\:\0]/g, "")
1875
+ ]));
1876
+ for (const possibleName of possibleNames) {
1877
+ const agentCognitiveDir = join(agentBase, possibleName);
1878
+ if (!isPathSafe(agentBase, agentCognitiveDir)) continue;
1879
+ try {
1880
+ await access(agentCognitiveDir);
1881
+ found = true;
1882
+ break;
1883
+ } catch {}
1884
+ }
1885
+ if (!found) try {
1886
+ const agentEntries = await readdir(agentBase, { withFileTypes: true });
1887
+ for (const agentEntry of agentEntries) {
1888
+ if (!agentEntry.isDirectory()) continue;
1889
+ const candidateDir = join(agentBase, agentEntry.name);
1890
+ if (!isPathSafe(agentBase, candidateDir)) continue;
1891
+ try {
1892
+ const candidateMdPath = join(candidateDir, fileName);
1893
+ await stat(candidateMdPath);
1894
+ const candidateParsed = cognitiveType === "skill" ? await parseSkillMd(candidateMdPath) : await parseCognitiveMd(candidateMdPath, cognitiveType);
1895
+ if (candidateParsed && candidateParsed.name === parsed.name) {
1896
+ found = true;
1897
+ break;
1898
+ }
1899
+ } catch {}
1900
+ }
1901
+ } catch {}
1902
+ if (found) installedAgents.push(agentType);
1903
+ }
1904
+ if (cognitivesMap.has(cognitiveKey)) {
1905
+ const existing = cognitivesMap.get(cognitiveKey);
1906
+ for (const agent of installedAgents) if (!existing.agents.includes(agent)) existing.agents.push(agent);
1907
+ } else cognitivesMap.set(cognitiveKey, {
1908
+ name: parsed.name,
1909
+ description: parsed.description,
1910
+ path: cognitiveDir,
1911
+ canonicalPath: cognitiveDir,
1912
+ scope: scopeKey,
1913
+ agents: installedAgents,
1914
+ cognitiveType
1915
+ });
1916
+ }
1917
+ } catch {}
1918
+ }
1919
+ return Array.from(cognitivesMap.values());
1920
+ }
1921
+ async function listInstalledSkills(options = {}) {
1922
+ return listInstalledCognitives({
1923
+ ...options,
1924
+ typeFilter: ["skill"]
1925
+ });
1926
+ }
1927
+ async function isCognitiveInstalled(name, agentType, cognitiveType, options = {}) {
1928
+ const sanitized = sanitizeName(name);
1929
+ const globalDir = getCognitiveDir(agentType, cognitiveType, "global");
1930
+ if (options.global && globalDir === void 0) return false;
1931
+ const localDir = getCognitiveDir(agentType, cognitiveType, "local");
1932
+ const targetBase = options.global ? globalDir : join(options.cwd || process.cwd(), localDir);
1933
+ const cognitiveDir = join(targetBase, sanitized);
1934
+ if (!isPathSafe(targetBase, cognitiveDir)) return false;
1935
+ try {
1936
+ await access(cognitiveDir);
1937
+ return true;
1938
+ } catch {
1939
+ return false;
1940
+ }
1941
+ }
1942
+ async function isSkillInstalled(skillName, agentType, options = {}) {
1943
+ return isCognitiveInstalled(skillName, agentType, options.cognitiveType ?? "skill", options);
1944
+ }
1945
+ const TELEMETRY_URL = "https://add-skill.vercel.sh/t";
1946
+ let cliVersion = null;
1947
+ function isCI() {
1948
+ return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.BUILDKITE || process.env.JENKINS_URL || process.env.TEAMCITY_VERSION);
1949
+ }
1950
+ function isEnabled() {
1951
+ return !process.env.DISABLE_TELEMETRY && !process.env.DO_NOT_TRACK;
1952
+ }
1953
+ function setVersion(version) {
1954
+ cliVersion = version;
1955
+ }
1956
+ function track(data) {
1957
+ if (!isEnabled()) return;
1958
+ try {
1959
+ const params = new URLSearchParams();
1960
+ if (cliVersion) params.set("v", cliVersion);
1961
+ if (isCI()) params.set("ci", "1");
1962
+ for (const [key, value] of Object.entries(data)) if (value !== void 0 && value !== null) params.set(key, String(value));
1963
+ fetch(`${TELEMETRY_URL}?${params.toString()}`).catch(() => {});
1964
+ } catch {}
1965
+ }
1966
+ var ProviderRegistryImpl = class {
1967
+ providers = [];
1968
+ register(provider) {
1969
+ if (this.providers.some((p) => p.id === provider.id)) throw new Error(`Provider with id "${provider.id}" already registered`);
1970
+ this.providers.push(provider);
1971
+ }
1972
+ findProvider(url) {
1973
+ for (const provider of this.providers) if (provider.match(url).matches) return provider;
1974
+ return null;
1975
+ }
1976
+ getProviders() {
1977
+ return [...this.providers];
1978
+ }
1979
+ };
1980
+ const registry = new ProviderRegistryImpl();
1981
+ function registerProvider(provider) {
1982
+ registry.register(provider);
1983
+ }
1984
+ function findProvider(url) {
1985
+ return registry.findProvider(url);
1986
+ }
1987
+ var MintlifyProvider = class {
1988
+ id = "mintlify";
1989
+ displayName = "Mintlify";
1990
+ match(url) {
1991
+ if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
1992
+ if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
1993
+ if (url.includes("github.com") || url.includes("gitlab.com")) return { matches: false };
1994
+ if (url.includes("huggingface.co")) return { matches: false };
1995
+ return { matches: true };
1996
+ }
1997
+ async fetchSkill(url) {
1998
+ try {
1999
+ const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
2000
+ if (!response.ok) return null;
2001
+ const content = await response.text();
2002
+ const { data } = (0, import_gray_matter.default)(content);
2003
+ const mintlifySite = data.metadata?.["mintlify-proj"];
2004
+ if (!mintlifySite) return null;
2005
+ if (!data.name || !data.description) return null;
2006
+ return {
2007
+ name: data.name,
2008
+ description: data.description,
2009
+ content,
2010
+ installName: mintlifySite,
2011
+ sourceUrl: url,
2012
+ metadata: data.metadata
2013
+ };
2014
+ } catch {
2015
+ return null;
2016
+ }
2017
+ }
2018
+ toRawUrl(url) {
2019
+ return url;
2020
+ }
2021
+ getSourceIdentifier(url) {
2022
+ return "mintlify/com";
2023
+ }
2024
+ };
2025
+ const mintlifyProvider = new MintlifyProvider();
2026
+ var HuggingFaceProvider = class {
2027
+ id = "huggingface";
2028
+ displayName = "HuggingFace";
2029
+ HOST = "huggingface.co";
2030
+ match(url) {
2031
+ if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
2032
+ try {
2033
+ if (new URL(url).hostname !== this.HOST) return { matches: false };
2034
+ } catch {
2035
+ return { matches: false };
2036
+ }
2037
+ if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
2038
+ if (!url.includes("/spaces/")) return { matches: false };
2039
+ return { matches: true };
2040
+ }
2041
+ async fetchSkill(url) {
2042
+ try {
2043
+ const rawUrl = this.toRawUrl(url);
2044
+ const response = await fetch(rawUrl, { signal: AbortSignal.timeout(3e4) });
2045
+ if (!response.ok) return null;
2046
+ const content = await response.text();
2047
+ const { data } = (0, import_gray_matter.default)(content);
2048
+ if (!data.name || !data.description) return null;
2049
+ const parsed = this.parseUrl(url);
2050
+ if (!parsed) return null;
2051
+ const installName = data.metadata?.["install-name"] || parsed.repo;
2052
+ return {
2053
+ name: data.name,
2054
+ description: data.description,
2055
+ content,
2056
+ installName,
2057
+ sourceUrl: url,
2058
+ metadata: data.metadata
2059
+ };
2060
+ } catch {
2061
+ return null;
2062
+ }
2063
+ }
2064
+ toRawUrl(url) {
2065
+ return url.replace("/blob/", "/raw/");
2066
+ }
2067
+ getSourceIdentifier(url) {
2068
+ const parsed = this.parseUrl(url);
2069
+ if (!parsed) return "huggingface/unknown";
2070
+ return `huggingface/${parsed.owner}/${parsed.repo}`;
2071
+ }
2072
+ parseUrl(url) {
2073
+ const match = url.match(/\/spaces\/([^/]+)\/([^/]+)/);
2074
+ if (!match || !match[1] || !match[2]) return null;
2075
+ return {
2076
+ owner: match[1],
2077
+ repo: match[2]
2078
+ };
2079
+ }
2080
+ };
2081
+ const huggingFaceProvider = new HuggingFaceProvider();
2082
+ var WellKnownProvider = class {
2083
+ id = "well-known";
2084
+ displayName = "Well-Known Skills";
2085
+ WELL_KNOWN_PATH = ".well-known/skills";
2086
+ INDEX_FILE = "index.json";
2087
+ match(url) {
2088
+ if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
2089
+ try {
2090
+ const parsed = new URL(url);
2091
+ if ([
2092
+ "github.com",
2093
+ "gitlab.com",
2094
+ "huggingface.co"
2095
+ ].includes(parsed.hostname)) return { matches: false };
2096
+ return {
2097
+ matches: true,
2098
+ sourceIdentifier: `wellknown/${parsed.hostname}`
2099
+ };
2100
+ } catch {
2101
+ return { matches: false };
2102
+ }
2103
+ }
2104
+ async fetchIndex(baseUrl) {
2105
+ try {
2106
+ const parsed = new URL(baseUrl);
2107
+ const basePath = parsed.pathname.replace(/\/$/, "");
2108
+ const urlsToTry = [{
2109
+ indexUrl: `${parsed.protocol}//${parsed.host}${basePath}/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`,
2110
+ baseUrl: `${parsed.protocol}//${parsed.host}${basePath}`
2111
+ }];
2112
+ if (basePath && basePath !== "") urlsToTry.push({
2113
+ indexUrl: `${parsed.protocol}//${parsed.host}/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`,
2114
+ baseUrl: `${parsed.protocol}//${parsed.host}`
2115
+ });
2116
+ for (const { indexUrl, baseUrl: resolvedBase } of urlsToTry) try {
2117
+ const response = await fetch(indexUrl);
2118
+ if (!response.ok) continue;
2119
+ const index = await response.json();
2120
+ if (!index.skills || !Array.isArray(index.skills)) continue;
2121
+ let allValid = true;
2122
+ for (const entry of index.skills) if (!this.isValidSkillEntry(entry)) {
2123
+ allValid = false;
2124
+ break;
2125
+ }
2126
+ if (allValid) return {
2127
+ index,
2128
+ resolvedBaseUrl: resolvedBase
2129
+ };
2130
+ } catch {
2131
+ continue;
2132
+ }
2133
+ return null;
2134
+ } catch {
2135
+ return null;
2136
+ }
2137
+ }
2138
+ isValidSkillEntry(entry) {
2139
+ if (!entry || typeof entry !== "object") return false;
2140
+ const e = entry;
2141
+ if (typeof e.name !== "string" || !e.name) return false;
2142
+ if (typeof e.description !== "string" || !e.description) return false;
2143
+ if (!Array.isArray(e.files) || e.files.length === 0) return false;
2144
+ if (!/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(e.name) && e.name.length > 1) {
2145
+ if (e.name.length === 1 && !/^[a-z0-9]$/.test(e.name)) return false;
2146
+ }
2147
+ for (const file of e.files) {
2148
+ if (typeof file !== "string") return false;
2149
+ if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
2150
+ }
2151
+ if (!e.files.some((f) => typeof f === "string" && f.toLowerCase() === "skill.md")) return false;
2152
+ return true;
2153
+ }
2154
+ async fetchSkill(url) {
2155
+ try {
2156
+ const parsed = new URL(url);
2157
+ const result = await this.fetchIndex(url);
2158
+ if (!result) return null;
2159
+ const { index, resolvedBaseUrl } = result;
2160
+ let skillName = null;
2161
+ const pathMatch = parsed.pathname.match(/\/.well-known\/skills\/([^/]+)\/?$/);
2162
+ if (pathMatch && pathMatch[1] && pathMatch[1] !== "index.json") skillName = pathMatch[1];
2163
+ else if (index.skills.length === 1) skillName = index.skills[0].name;
2164
+ if (!skillName) return null;
2165
+ const skillEntry = index.skills.find((s) => s.name === skillName);
2166
+ if (!skillEntry) return null;
2167
+ return this.fetchSkillByEntry(resolvedBaseUrl, skillEntry);
2168
+ } catch {
2169
+ return null;
2170
+ }
2171
+ }
2172
+ async fetchSkillByEntry(baseUrl, entry) {
2173
+ try {
2174
+ const skillBaseUrl = `${baseUrl.replace(/\/$/, "")}/${this.WELL_KNOWN_PATH}/${entry.name}`;
2175
+ const skillMdUrl = `${skillBaseUrl}/SKILL.md`;
2176
+ const response = await fetch(skillMdUrl);
2177
+ if (!response.ok) return null;
2178
+ const content = await response.text();
2179
+ const { data } = (0, import_gray_matter.default)(content);
2180
+ if (!data.name || !data.description) return null;
2181
+ const files = /* @__PURE__ */ new Map();
2182
+ files.set("SKILL.md", content);
2183
+ const filePromises = entry.files.filter((f) => f.toLowerCase() !== "skill.md").map(async (filePath) => {
2184
+ try {
2185
+ const fileUrl = `${skillBaseUrl}/${filePath}`;
2186
+ const fileResponse = await fetch(fileUrl);
2187
+ if (fileResponse.ok) return {
2188
+ path: filePath,
2189
+ content: await fileResponse.text()
2190
+ };
2191
+ } catch {}
2192
+ return null;
2193
+ });
2194
+ const fileResults = await Promise.all(filePromises);
2195
+ for (const result of fileResults) if (result) files.set(result.path, result.content);
2196
+ return {
2197
+ name: data.name,
2198
+ description: data.description,
2199
+ content,
2200
+ installName: entry.name,
2201
+ sourceUrl: skillMdUrl,
2202
+ metadata: data.metadata,
2203
+ files,
2204
+ indexEntry: entry
2205
+ };
2206
+ } catch {
2207
+ return null;
2208
+ }
2209
+ }
2210
+ async fetchAllSkills(url) {
2211
+ try {
2212
+ const result = await this.fetchIndex(url);
2213
+ if (!result) return [];
2214
+ const { index, resolvedBaseUrl } = result;
2215
+ const skillPromises = index.skills.map((entry) => this.fetchSkillByEntry(resolvedBaseUrl, entry));
2216
+ return (await Promise.all(skillPromises)).filter((s) => s !== null);
2217
+ } catch {
2218
+ return [];
2219
+ }
2220
+ }
2221
+ toRawUrl(url) {
2222
+ try {
2223
+ const parsed = new URL(url);
2224
+ if (url.toLowerCase().endsWith("/skill.md")) return url;
2225
+ const pathMatch = parsed.pathname.match(/\/.well-known\/skills\/([^/]+)\/?$/);
2226
+ if (pathMatch && pathMatch[1]) {
2227
+ const basePath = parsed.pathname.replace(/\/.well-known\/skills\/.*$/, "");
2228
+ return `${parsed.protocol}//${parsed.host}${basePath}/${this.WELL_KNOWN_PATH}/${pathMatch[1]}/SKILL.md`;
2229
+ }
2230
+ const basePath = parsed.pathname.replace(/\/$/, "");
2231
+ return `${parsed.protocol}//${parsed.host}${basePath}/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`;
2232
+ } catch {
2233
+ return url;
2234
+ }
2235
+ }
2236
+ getSourceIdentifier(url) {
2237
+ try {
2238
+ const parsed = new URL(url);
2239
+ const hostParts = parsed.hostname.split(".");
2240
+ if (hostParts.length >= 2) {
2241
+ const tld = hostParts[hostParts.length - 1];
2242
+ return `${hostParts[hostParts.length - 2]}/${tld}`;
2243
+ }
2244
+ return parsed.hostname.replace(".", "/");
2245
+ } catch {
2246
+ return "unknown/unknown";
2247
+ }
2248
+ }
2249
+ async hasSkillsIndex(url) {
2250
+ return await this.fetchIndex(url) !== null;
2251
+ }
2252
+ };
2253
+ const wellKnownProvider = new WellKnownProvider();
2254
+ registerProvider(mintlifyProvider);
2255
+ registerProvider(huggingFaceProvider);
2256
+ async function fetchMintlifySkill(url) {
2257
+ try {
2258
+ const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
2259
+ if (!response.ok) return null;
2260
+ const content = await response.text();
2261
+ const { data } = (0, import_gray_matter.default)(content);
2262
+ const mintlifySite = data.metadata?.["mintlify-proj"];
2263
+ if (!mintlifySite) return null;
2264
+ if (!data.name || !data.description) return null;
2265
+ return {
2266
+ name: data.name,
2267
+ description: data.description,
2268
+ content,
2269
+ mintlifySite,
2270
+ sourceUrl: url
2271
+ };
2272
+ } catch {
2273
+ return null;
2274
+ }
2275
+ }
2276
+ var version$1 = "1.0.0";
2277
+ function initTelemetry(version) {
2278
+ setVersion(version);
2279
+ }
2280
+ setVersion(version$1);
2281
+ async function selectTargetAgents(options, spinner, cleanup, useUniversalAgents = false) {
2282
+ const validAgents = Object.keys(agents);
2283
+ const universalAgents = getUniversalAgents();
2284
+ if (options.agent?.includes("*")) {
2285
+ const all = validAgents;
2286
+ logger.info(`Installing to all ${all.length} agents`);
2287
+ return all;
2288
+ }
2289
+ if (options.agent && options.agent.length > 0) {
2290
+ const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
2291
+ if (invalidAgents.length > 0) {
2292
+ logger.error(`Invalid agents: ${invalidAgents.join(", ")}`);
2293
+ logger.info(`Valid agents: ${validAgents.join(", ")}`);
2294
+ await cleanup?.();
2295
+ process.exit(1);
2296
+ }
2297
+ if (useUniversalAgents) return ensureUniversalAgents(options.agent);
2298
+ return options.agent;
2299
+ }
2300
+ spinner.start("Loading agents...");
2301
+ const installedAgents = await detectInstalledAgents();
2302
+ const totalAgents = Object.keys(agents).length;
2303
+ spinner.succeed(`${totalAgents} agents`);
2304
+ if (installedAgents.length === 0) {
2305
+ if (options.yes) {
2306
+ if (useUniversalAgents) {
2307
+ logger.info(`Installing to universal agents`);
2308
+ return universalAgents;
2309
+ }
2310
+ const all = validAgents;
2311
+ logger.info("Installing to all agents");
2312
+ return all;
2313
+ }
2314
+ if (useUniversalAgents) {
2315
+ const selected = await selectAgentsInteractive({ global: options.global });
2316
+ if (pD(selected)) {
2317
+ logger.cancel("Installation cancelled");
2318
+ await cleanup?.();
2319
+ process.exit(0);
2320
+ }
2321
+ return selected;
2322
+ }
2323
+ logger.info("Select agents to install skills to");
2324
+ const selected = await promptForAgents("Which agents do you want to install to?", Object.entries(agents).map(([key, config]) => ({
2325
+ value: key,
2326
+ label: config.displayName
2327
+ })));
2328
+ if (pD(selected)) {
2329
+ logger.cancel("Installation cancelled");
2330
+ await cleanup?.();
2331
+ process.exit(0);
2332
+ }
2333
+ return selected;
2334
+ }
2335
+ if (installedAgents.length === 1 || options.yes) {
2336
+ if (useUniversalAgents) {
2337
+ const target = ensureUniversalAgents(installedAgents);
2338
+ const { universal, symlinked } = splitAgentsByType(target);
2339
+ if (symlinked.length > 0) logger.info(`Installing to: ${import_picocolors.default.green("universal")} + ${symlinked.map((a) => import_picocolors.default.cyan(a)).join(", ")}`);
2340
+ else logger.info(`Installing to: ${import_picocolors.default.green("universal agents")}`);
2341
+ return target;
2342
+ }
2343
+ if (installedAgents.length === 1) {
2344
+ const firstAgent = installedAgents[0];
2345
+ logger.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
2346
+ } else logger.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
2347
+ return installedAgents;
2348
+ }
2349
+ const selected = await selectAgentsInteractive({ global: options.global });
2350
+ if (pD(selected)) {
2351
+ logger.cancel("Installation cancelled");
2352
+ await cleanup?.();
2353
+ process.exit(0);
2354
+ }
2355
+ return selected;
2356
+ }
2357
+ async function selectInstallScope(options, targetAgents, cleanup) {
2358
+ let installGlobally = options.global ?? false;
2359
+ const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
2360
+ if (options.global === void 0 && !options.yes && supportsGlobal) {
2361
+ const scope = await ve({
2362
+ message: "Installation scope",
2363
+ options: [{
2364
+ value: false,
2365
+ label: "Project",
2366
+ hint: "Install in current directory (committed with your project)"
2367
+ }, {
2368
+ value: true,
2369
+ label: "Global",
2370
+ hint: "Install in home directory (available across all projects)"
2371
+ }]
2372
+ });
2373
+ if (pD(scope)) {
2374
+ logger.cancel("Installation cancelled");
2375
+ await cleanup?.();
2376
+ process.exit(0);
2377
+ }
2378
+ installGlobally = scope;
2379
+ }
2380
+ return installGlobally;
2381
+ }
2382
+ async function selectInstallMode(options, cleanup) {
2383
+ if (options.yes) return "symlink";
2384
+ const modeChoice = await ve({
2385
+ message: "Installation method",
2386
+ options: [{
2387
+ value: "symlink",
2388
+ label: "Symlink (Recommended)",
2389
+ hint: "Single source of truth, easy updates"
2390
+ }, {
2391
+ value: "copy",
2392
+ label: "Copy to all agents",
2393
+ hint: "Independent copies for each agent"
2394
+ }]
2395
+ });
2396
+ if (pD(modeChoice)) {
2397
+ logger.cancel("Installation cancelled");
2398
+ await cleanup?.();
2399
+ process.exit(0);
2400
+ }
2401
+ return modeChoice;
2402
+ }
2403
+ async function isSourcePrivate(source) {
2404
+ const ownerRepo = parseOwnerRepo(source);
2405
+ if (!ownerRepo) return false;
2406
+ return isRepoPrivate(ownerRepo.owner, ownerRepo.repo);
2407
+ }
2408
+ async function executeInstallFlow(prepared, options, spinner, cleanup) {
2409
+ const { items, targetAgents, installGlobally, installMode, cognitiveType } = prepared;
2410
+ const cwd = process.cwd();
2411
+ const overwriteChecks = await Promise.all(items.flatMap((item) => targetAgents.map(async (agent) => ({
2412
+ skillName: item.installName,
2413
+ agent,
2414
+ installed: cognitiveType === "skill" ? await isSkillInstalled(item.installName, agent, { global: installGlobally }) : await isCognitiveInstalled(item.installName, agent, cognitiveType, { global: installGlobally })
2415
+ }))));
2416
+ const overwriteStatus = /* @__PURE__ */ new Map();
2417
+ for (const { skillName, agent, installed } of overwriteChecks) {
2418
+ if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
2419
+ overwriteStatus.get(skillName).set(agent, installed);
2420
+ }
2421
+ const summaryLines = [];
2422
+ for (const item of items) {
2423
+ if (summaryLines.length > 0) summaryLines.push("");
2424
+ const shortCanonical = shortenPath$1(getCanonicalPath(item.installName, {
2425
+ global: installGlobally,
2426
+ cognitiveType
2427
+ }), cwd);
2428
+ summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
2429
+ summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
2430
+ if (item.fileCount && item.fileCount > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${item.fileCount}`);
2431
+ const skillOverwrites = overwriteStatus.get(item.installName);
2432
+ const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
2433
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
2434
+ }
2435
+ logger.note(summaryLines.join("\n"), "Installation Summary");
2436
+ if (!options.yes) {
2437
+ const confirmed = await ye({ message: "Proceed with installation?" });
2438
+ if (pD(confirmed) || !confirmed) {
2439
+ logger.cancel("Installation cancelled");
2440
+ await cleanup?.();
2441
+ process.exit(0);
2442
+ }
2443
+ }
2444
+ const label = items.length === 1 ? "Installing skill..." : "Installing skills...";
2445
+ spinner.start(label);
2446
+ const results = [];
2447
+ for (const item of items) for (const agent of targetAgents) {
2448
+ const result = await item.installFn(agent, {
2449
+ global: installGlobally,
2450
+ mode: installMode
2451
+ });
2452
+ results.push({
2453
+ skill: item.installName,
2454
+ agent: agents[agent].displayName,
2455
+ ...result
2456
+ });
2457
+ }
2458
+ spinner.succeed("Installation complete");
2459
+ logger.line();
2460
+ const successful = results.filter((r) => r.success);
2461
+ const failed = results.filter((r) => !r.success);
2462
+ const { telemetry } = prepared;
2463
+ if (telemetry.source) {
2464
+ let shouldTrack = true;
2465
+ if (telemetry.checkPrivacy) {
2466
+ if (await isSourcePrivate(telemetry.source) === true) shouldTrack = false;
2467
+ }
2468
+ if (shouldTrack) track({
2469
+ event: "install",
2470
+ source: telemetry.source,
2471
+ skills: items.map((i) => i.installName).join(","),
2472
+ agents: targetAgents.join(","),
2473
+ ...installGlobally && { global: "1" },
2474
+ skillFiles: JSON.stringify(telemetry.skillFiles),
2475
+ ...telemetry.sourceType && { sourceType: telemetry.sourceType }
2476
+ });
2477
+ }
2478
+ if (successful.length > 0 && installGlobally) {
2479
+ const successfulSkillNames = new Set(successful.map((r) => r.skill));
2480
+ for (const entry of prepared.lockEntries) if (successfulSkillNames.has(entry.name)) try {
2481
+ let skillFolderHash = entry.skillFolderHash;
2482
+ if (!skillFolderHash && entry.sourceType === "github" && entry.skillPath) {
2483
+ const hash = await fetchSkillFolderHash(entry.source, entry.skillPath);
2484
+ if (hash) skillFolderHash = hash;
2485
+ }
2486
+ if (entry.isCognitive) await addCognitiveToLock(entry.name, entry.cognitiveType, {
2487
+ source: entry.source,
2488
+ sourceType: entry.sourceType,
2489
+ sourceUrl: entry.sourceUrl,
2490
+ skillPath: entry.skillPath,
2491
+ skillFolderHash
2492
+ });
2493
+ else await addSkillToLock(entry.name, {
2494
+ source: entry.source,
2495
+ sourceType: entry.sourceType,
2496
+ sourceUrl: entry.sourceUrl,
2497
+ skillFolderHash
2498
+ });
2499
+ } catch {}
2500
+ }
2501
+ if (successful.length > 0) {
2502
+ const bySkill = /* @__PURE__ */ new Map();
2503
+ for (const r of successful) {
2504
+ const skillResults = bySkill.get(r.skill) || [];
2505
+ skillResults.push(r);
2506
+ bySkill.set(r.skill, skillResults);
2507
+ }
2508
+ const skillCount = bySkill.size;
2509
+ const resultLines = [];
2510
+ for (const [skillName, skillResults] of bySkill) {
2511
+ const firstResult = skillResults[0];
2512
+ if (firstResult.mode === "copy") {
2513
+ resultLines.push(`${import_picocolors.default.green("✓")} ${skillName} ${import_picocolors.default.dim("(copied)")}`);
2514
+ for (const r of skillResults) {
2515
+ const shortPath = shortenPath$1(r.path, cwd);
2516
+ resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
2517
+ }
2518
+ } else {
2519
+ if (firstResult.canonicalPath) {
2520
+ const shortPath = shortenPath$1(firstResult.canonicalPath, cwd);
2521
+ resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
2522
+ } else resultLines.push(`${import_picocolors.default.green("✓")} ${skillName}`);
2523
+ resultLines.push(...buildResultLines(skillResults, targetAgents));
2524
+ }
2525
+ }
2526
+ const title = import_picocolors.default.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
2527
+ logger.note(resultLines.join("\n"), title);
2528
+ const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
2529
+ if (symlinkFailures.length > 0) {
2530
+ const copiedAgentNames = symlinkFailures.map((r) => r.agent);
2531
+ logger.warning(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgentNames)}`));
2532
+ logger.message(import_picocolors.default.dim("Files were copied instead. On Windows, enable Developer Mode for symlink support."));
2533
+ }
2534
+ }
2535
+ if (failed.length > 0) {
2536
+ logger.line();
2537
+ logger.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
2538
+ for (const r of failed) logger.message(`${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
2539
+ }
2540
+ logger.outro(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
2541
+ await promptForFindSkills(options, targetAgents);
2542
+ }
2543
+ async function selectSkillItems(items, options, cleanup) {
2544
+ if (options.skill?.includes("*")) {
2545
+ logger.info(`Installing all ${items.length} skills`);
2546
+ return items;
2547
+ }
2548
+ if (options.skill && options.skill.length > 0) {
2549
+ const selected = items.filter((s) => options.skill.some((name) => "installName" in s && s.installName?.toLowerCase() === name.toLowerCase() || s.name.toLowerCase() === name.toLowerCase()));
2550
+ if (selected.length === 0) {
2551
+ logger.error(`No matching skills found for: ${options.skill.join(", ")}`);
2552
+ logger.info("Available skills:");
2553
+ for (const s of items) logger.message(`- ${"installName" in s ? s.installName : s.name}`);
2554
+ await cleanup?.();
2555
+ process.exit(1);
2556
+ }
2557
+ logger.info(`Selected ${selected.length} skill${selected.length !== 1 ? "s" : ""}: ${selected.map((s) => import_picocolors.default.cyan("installName" in s ? s.installName : s.name)).join(", ")}`);
2558
+ return selected;
2559
+ }
2560
+ if (items.length === 1) {
2561
+ const first = items[0];
2562
+ logger.info(`Skill: ${import_picocolors.default.cyan("installName" in first ? first.installName : first.name)}`);
2563
+ return items;
2564
+ }
2565
+ if (options.yes) {
2566
+ logger.info(`Installing all ${items.length} skills`);
2567
+ return items;
2568
+ }
2569
+ const selected = await multiselect({
2570
+ message: "Select skills to install",
2571
+ options: items.map((s) => ({
2572
+ value: s,
2573
+ label: "installName" in s ? s.installName : s.name,
2574
+ hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
2575
+ })),
2576
+ required: true
2577
+ });
2578
+ if (pD(selected)) {
2579
+ logger.cancel("Installation cancelled");
2580
+ await cleanup?.();
2581
+ process.exit(0);
2582
+ }
2583
+ return selected;
2584
+ }
2585
+ async function resolveRemoteSkill(source, url, options, spinner) {
2586
+ const provider = findProvider(url);
2587
+ if (!provider) return resolveDirectUrlLegacy(source, url, options, spinner);
2588
+ spinner.start(`Fetching skill.md from ${provider.displayName}...`);
2589
+ const providerSkill = await provider.fetchSkill(url);
2590
+ if (!providerSkill) {
2591
+ spinner.fail("Invalid skill");
2592
+ logger.outro(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description)."));
2593
+ process.exit(1);
2594
+ }
2595
+ const remoteSkill = {
2596
+ name: providerSkill.name,
2597
+ description: providerSkill.description,
2598
+ content: providerSkill.content,
2599
+ installName: providerSkill.installName,
2600
+ sourceUrl: providerSkill.sourceUrl,
2601
+ providerId: provider.id,
2602
+ sourceIdentifier: provider.getSourceIdentifier(url),
2603
+ metadata: providerSkill.metadata
2604
+ };
2605
+ spinner.succeed(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
2606
+ logger.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
2607
+ logger.message(import_picocolors.default.dim(remoteSkill.description));
2608
+ logger.message(import_picocolors.default.dim(`Source: ${remoteSkill.sourceIdentifier}`));
2609
+ if (options.list) {
2610
+ logger.line();
2611
+ logger.step(import_picocolors.default.bold("Skill Details"));
2612
+ logger.message(`${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
2613
+ logger.message(`${import_picocolors.default.cyan("Install as:")} ${remoteSkill.installName}`);
2614
+ logger.message(`${import_picocolors.default.cyan("Provider:")} ${provider.displayName}`);
2615
+ logger.message(`${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
2616
+ logger.outro("Run without --list to install");
2617
+ process.exit(0);
2618
+ }
2619
+ const targetAgents = await selectTargetAgents(options, spinner, void 0, true);
2620
+ const installGlobally = await selectInstallScope(options, targetAgents);
2621
+ const installMode = await selectInstallMode(options);
2622
+ return {
2623
+ items: [{
2624
+ installName: remoteSkill.installName,
2625
+ displayName: remoteSkill.name,
2626
+ description: remoteSkill.description,
2627
+ sourceIdentifier: remoteSkill.sourceIdentifier,
2628
+ providerId: remoteSkill.providerId,
2629
+ sourceUrl: url,
2630
+ installFn: (agent, opts) => installRemoteSkillForAgent(remoteSkill, agent, opts)
2631
+ }],
2632
+ targetAgents,
2633
+ installGlobally,
2634
+ installMode,
2635
+ cognitiveType: "skill",
2636
+ lockEntries: [{
2637
+ name: remoteSkill.installName,
2638
+ source: remoteSkill.sourceIdentifier,
2639
+ sourceType: remoteSkill.providerId,
2640
+ sourceUrl: url,
2641
+ skillFolderHash: "",
2642
+ cognitiveType: "skill",
2643
+ isCognitive: false
2644
+ }],
2645
+ telemetry: {
2646
+ source: remoteSkill.sourceIdentifier,
2647
+ sourceType: remoteSkill.providerId,
2648
+ skillFiles: { [remoteSkill.installName]: url },
2649
+ checkPrivacy: true
2650
+ }
2651
+ };
2652
+ }
2653
+ async function resolveDirectUrlLegacy(source, url, options, spinner) {
2654
+ spinner.start("Fetching skill.md...");
2655
+ const mintlifySkill = await fetchMintlifySkill(url);
2656
+ if (!mintlifySkill) {
2657
+ spinner.fail("Invalid skill");
2658
+ logger.outro(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description, mintlify-proj)."));
2659
+ process.exit(1);
2660
+ }
2661
+ const remoteSkill = {
2662
+ name: mintlifySkill.name,
2663
+ description: mintlifySkill.description,
2664
+ content: mintlifySkill.content,
2665
+ installName: mintlifySkill.mintlifySite,
2666
+ sourceUrl: mintlifySkill.sourceUrl,
2667
+ providerId: "mintlify",
2668
+ sourceIdentifier: "mintlify/com"
2669
+ };
2670
+ spinner.succeed(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
2671
+ logger.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
2672
+ logger.message(import_picocolors.default.dim(remoteSkill.description));
2673
+ if (options.list) {
2674
+ logger.line();
2675
+ logger.step(import_picocolors.default.bold("Skill Details"));
2676
+ logger.message(`${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
2677
+ logger.message(`${import_picocolors.default.cyan("Site:")} ${remoteSkill.installName}`);
2678
+ logger.message(`${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
2679
+ logger.outro("Run without --list to install");
2680
+ process.exit(0);
2681
+ }
2682
+ const targetAgents = await selectTargetAgents(options, spinner);
2683
+ const installGlobally = await selectInstallScope(options, targetAgents);
2684
+ return {
2685
+ items: [{
2686
+ installName: remoteSkill.installName,
2687
+ displayName: remoteSkill.name,
2688
+ description: remoteSkill.description,
2689
+ sourceIdentifier: "mintlify/com",
2690
+ providerId: "mintlify",
2691
+ sourceUrl: url,
2692
+ installFn: (agent, opts) => installRemoteSkillForAgent(remoteSkill, agent, opts)
2693
+ }],
2694
+ targetAgents,
2695
+ installGlobally,
2696
+ installMode: "symlink",
2697
+ cognitiveType: "skill",
2698
+ lockEntries: [{
2699
+ name: remoteSkill.installName,
2700
+ source: `mintlify/${remoteSkill.installName}`,
2701
+ sourceType: "mintlify",
2702
+ sourceUrl: url,
2703
+ skillFolderHash: "",
2704
+ cognitiveType: "skill",
2705
+ isCognitive: false
2706
+ }],
2707
+ telemetry: {
2708
+ source: "mintlify/com",
2709
+ sourceType: "mintlify",
2710
+ skillFiles: { [remoteSkill.installName]: url },
2711
+ checkPrivacy: false
2712
+ }
2713
+ };
2714
+ }
2715
+ async function resolveWellKnownSkills(source, url, options, spinner) {
2716
+ spinner.start("Discovering skills from well-known endpoint...");
2717
+ const skills = await wellKnownProvider.fetchAllSkills(url);
2718
+ if (skills.length === 0) {
2719
+ spinner.fail("No skills found");
2720
+ logger.outro(import_picocolors.default.red("No skills found at this URL. Make sure the server has a /.well-known/skills/index.json file."));
2721
+ process.exit(1);
2722
+ }
2723
+ spinner.succeed(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
2724
+ for (const skill of skills) {
2725
+ logger.info(`Skill: ${import_picocolors.default.cyan(skill.installName)}`);
2726
+ logger.message(import_picocolors.default.dim(skill.description));
2727
+ if (skill.files.size > 1) logger.message(import_picocolors.default.dim(` Files: ${Array.from(skill.files.keys()).join(", ")}`));
2728
+ }
2729
+ if (options.list) {
2730
+ logger.line();
2731
+ logger.step(import_picocolors.default.bold("Available Skills"));
2732
+ for (const skill of skills) {
2733
+ logger.message(`${import_picocolors.default.cyan(skill.installName)}`);
2734
+ logger.message(` ${import_picocolors.default.dim(skill.description)}`);
2735
+ if (skill.files.size > 1) logger.message(` ${import_picocolors.default.dim(`Files: ${skill.files.size}`)}`);
2736
+ }
2737
+ logger.outro("Run without --list to install");
2738
+ process.exit(0);
2739
+ }
2740
+ const selectedSkills = await selectSkillItems(skills, options);
2741
+ const targetAgents = await selectTargetAgents(options, spinner);
2742
+ const installGlobally = await selectInstallScope(options, targetAgents);
2743
+ const installMode = await selectInstallMode(options);
2744
+ const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
2745
+ const items = selectedSkills.map((skill) => ({
2746
+ installName: skill.installName,
2747
+ displayName: skill.name,
2748
+ description: skill.description,
2749
+ sourceIdentifier,
2750
+ providerId: "well-known",
2751
+ sourceUrl: skill.sourceUrl,
2752
+ fileCount: skill.files.size,
2753
+ installFn: (agent, opts) => installWellKnownSkillForAgent(skill, agent, opts)
2754
+ }));
2755
+ const skillFiles = {};
2756
+ const lockEntries = [];
2757
+ for (const skill of selectedSkills) {
2758
+ skillFiles[skill.installName] = skill.sourceUrl;
2759
+ lockEntries.push({
2760
+ name: skill.installName,
2761
+ source: sourceIdentifier,
2762
+ sourceType: "well-known",
2763
+ sourceUrl: skill.sourceUrl,
2764
+ skillFolderHash: "",
2765
+ cognitiveType: "skill",
2766
+ isCognitive: false
2767
+ });
2768
+ }
2769
+ return {
2770
+ items,
2771
+ targetAgents,
2772
+ installGlobally,
2773
+ installMode,
2774
+ cognitiveType: "skill",
2775
+ lockEntries,
2776
+ telemetry: {
2777
+ source: sourceIdentifier,
2778
+ sourceType: "well-known",
2779
+ skillFiles,
2780
+ checkPrivacy: true
2781
+ }
2782
+ };
2783
+ }
2784
+ async function resolveGitRepoSkills(source, parsed, options, spinner, skillsDir, tempDir) {
2785
+ const cleanupFn = async () => {
2786
+ await cleanupDir(tempDir);
2787
+ };
2788
+ const cognitiveType = options.type ?? "skill";
2789
+ const cognitiveLabel = cognitiveType === "skill" ? "skills" : `${cognitiveType}s`;
2790
+ const cognitiveFile = cognitiveType === "skill" ? "SKILL.md" : cognitiveType === "agent" ? "AGENT.md" : "PROMPT.md";
2791
+ const includeInternal = !!(options.skill && options.skill.length > 0);
2792
+ spinner.start(`Discovering ${cognitiveLabel}...`);
2793
+ const skills = options.type ? await discoverCognitives(skillsDir, parsed.subpath, {
2794
+ includeInternal,
2795
+ fullDepth: options.fullDepth,
2796
+ types: [options.type]
2797
+ }) : await discoverSkills(skillsDir, parsed.subpath, {
2798
+ includeInternal,
2799
+ fullDepth: options.fullDepth
2800
+ });
2801
+ if (skills.length === 0) {
2802
+ spinner.fail(`No ${cognitiveLabel} found`);
2803
+ logger.outro(import_picocolors.default.red(`No valid ${cognitiveLabel} found. They require a ${cognitiveFile} with name and description.`));
2804
+ await cleanupFn();
2805
+ process.exit(1);
2806
+ }
2807
+ spinner.succeed(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
2808
+ if (options.list) {
2809
+ logger.line();
2810
+ logger.step(import_picocolors.default.bold("Available Skills"));
2811
+ for (const skill of skills) {
2812
+ logger.message(`${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2813
+ logger.message(` ${import_picocolors.default.dim(skill.description)}`);
2814
+ }
2815
+ logger.outro("Use --skill <name> to install specific skills");
2816
+ await cleanupFn();
2817
+ process.exit(0);
2818
+ }
2819
+ let selectedSkills;
2820
+ if (options.skill?.includes("*")) {
2821
+ selectedSkills = skills;
2822
+ logger.info(`Installing all ${skills.length} skills`);
2823
+ } else if (options.skill && options.skill.length > 0) {
2824
+ selectedSkills = filterSkills(skills, options.skill);
2825
+ if (selectedSkills.length === 0) {
2826
+ logger.error(`No matching skills found for: ${options.skill.join(", ")}`);
2827
+ logger.info("Available skills:");
2828
+ for (const s of skills) logger.message(`- ${getSkillDisplayName(s)}`);
2829
+ await cleanupFn();
2830
+ process.exit(1);
2831
+ }
2832
+ logger.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${selectedSkills.map((s) => import_picocolors.default.cyan(getSkillDisplayName(s))).join(", ")}`);
2833
+ } else if (skills.length === 1) {
2834
+ selectedSkills = skills;
2835
+ const firstSkill = skills[0];
2836
+ logger.info(`Skill: ${import_picocolors.default.cyan(getSkillDisplayName(firstSkill))}`);
2837
+ logger.message(import_picocolors.default.dim(firstSkill.description));
2838
+ } else if (options.yes) {
2839
+ selectedSkills = skills;
2840
+ logger.info(`Installing all ${skills.length} skills`);
2841
+ } else {
2842
+ const selected = await multiselect({
2843
+ message: "Select skills to install",
2844
+ options: skills.map((s) => ({
2845
+ value: s,
2846
+ label: getSkillDisplayName(s),
2847
+ hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
2848
+ })),
2849
+ required: true
2850
+ });
2851
+ if (pD(selected)) {
2852
+ logger.cancel("Installation cancelled");
2853
+ await cleanupFn();
2854
+ process.exit(0);
2855
+ }
2856
+ selectedSkills = selected;
2857
+ }
2858
+ const targetAgents = await selectTargetAgents(options, spinner, cleanupFn);
2859
+ const installGlobally = await selectInstallScope(options, targetAgents, cleanupFn);
2860
+ const installMode = await selectInstallMode(options, cleanupFn);
2861
+ const normalizedSource = getOwnerRepo(parsed);
2862
+ const skillFiles = {};
2863
+ for (const skill of selectedSkills) {
2864
+ let relativePath;
2865
+ if (tempDir && skill.path === tempDir) relativePath = cognitiveFile;
2866
+ else if (tempDir && skill.path.startsWith(tempDir + sep)) relativePath = skill.path.slice(tempDir.length + 1).split(sep).join("/") + `/${cognitiveFile}`;
2867
+ else continue;
2868
+ skillFiles[skill.name] = relativePath;
2869
+ }
2870
+ const items = selectedSkills.map((skill) => ({
2871
+ installName: skill.name,
2872
+ displayName: getSkillDisplayName(skill),
2873
+ description: skill.description,
2874
+ sourceIdentifier: normalizedSource ?? source,
2875
+ providerId: parsed.type,
2876
+ sourceUrl: parsed.url,
2877
+ installFn: (agent, opts) => installCognitiveForAgent(skill, agent, {
2878
+ ...opts,
2879
+ cognitiveType
2880
+ })
2881
+ }));
2882
+ const lockEntries = [];
2883
+ if (normalizedSource) for (const skill of selectedSkills) lockEntries.push({
2884
+ name: getSkillDisplayName(skill),
2885
+ source: normalizedSource,
2886
+ sourceType: parsed.type,
2887
+ sourceUrl: parsed.url,
2888
+ skillPath: skillFiles[skill.name],
2889
+ skillFolderHash: "",
2890
+ cognitiveType,
2891
+ isCognitive: true
2892
+ });
2893
+ let telemetrySource = normalizedSource ?? "";
2894
+ if (normalizedSource) {
2895
+ const ownerRepo = parseOwnerRepo(normalizedSource);
2896
+ if (ownerRepo) if (await isRepoPrivate(ownerRepo.owner, ownerRepo.repo) === false) {} else telemetrySource = "";
2897
+ }
2898
+ return {
2899
+ items,
2900
+ targetAgents,
2901
+ installGlobally,
2902
+ installMode,
2903
+ cognitiveType,
2904
+ lockEntries,
2905
+ telemetry: {
2906
+ source: telemetrySource,
2907
+ skillFiles,
2908
+ checkPrivacy: false
2909
+ }
2910
+ };
2911
+ }
2912
+ async function runAdd(args, options = {}) {
2913
+ const source = args[0];
2914
+ let installTipShown = false;
2915
+ const showInstallTip = () => {
2916
+ if (installTipShown) return;
2917
+ logger.message(import_picocolors.default.dim("Tip: use the --yes (-y) and --global (-g) flags to install without prompts."));
2918
+ installTipShown = true;
2919
+ };
2920
+ if (!source) {
2921
+ logger.line();
2922
+ logger.log(import_picocolors.default.bgRed(import_picocolors.default.white(import_picocolors.default.bold(" ERROR "))) + " " + import_picocolors.default.red("Missing required argument: source"));
2923
+ logger.line();
2924
+ logger.dim(" Usage:");
2925
+ logger.log(` ${import_picocolors.default.cyan("npx synk add")} ${import_picocolors.default.yellow("<source>")} ${import_picocolors.default.dim("[options]")}`);
2926
+ logger.line();
2927
+ logger.dim(" Example:");
2928
+ logger.log(` ${import_picocolors.default.cyan("npx synk add")} ${import_picocolors.default.yellow("vercel-labs/agent-skills")}`);
2929
+ logger.line();
2930
+ process.exit(1);
2931
+ }
2932
+ if (options.all) {
2933
+ options.skill = ["*"];
2934
+ options.agent = ["*"];
2935
+ options.yes = true;
2936
+ }
2937
+ logger.intro(" synk ");
2938
+ if (!process.stdin.isTTY) showInstallTip();
2939
+ let tempDir = null;
2940
+ try {
2941
+ const spinner = logger.spinner();
2942
+ spinner.start("Parsing source...");
2943
+ const parsed = parseSource(source);
2944
+ spinner.succeed(`Source: ${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
2945
+ if (parsed.type === "direct-url") {
2946
+ await executeInstallFlow(await resolveRemoteSkill(source, parsed.url, options, spinner), options, spinner);
2947
+ return;
2948
+ }
2949
+ if (parsed.type === "well-known") {
2950
+ await executeInstallFlow(await resolveWellKnownSkills(source, parsed.url, options, spinner), options, spinner);
2951
+ return;
2952
+ }
2953
+ let skillsDir;
2954
+ if (parsed.type === "local") {
2955
+ spinner.start("Validating local path...");
2956
+ if (!existsSync(parsed.localPath)) {
2957
+ spinner.fail("Path not found");
2958
+ logger.outro(import_picocolors.default.red(`Local path does not exist: ${parsed.localPath}`));
2959
+ process.exit(1);
2960
+ }
2961
+ skillsDir = parsed.localPath;
2962
+ spinner.succeed("Local path validated");
2963
+ } else {
2964
+ spinner.start("Cloning repository...");
2965
+ tempDir = await cloneRepo(parsed.url, parsed.ref);
2966
+ skillsDir = tempDir;
2967
+ spinner.succeed("Repository cloned");
2968
+ }
2969
+ if (parsed.skillFilter) {
2970
+ options.skill = options.skill || [];
2971
+ if (!options.skill.includes(parsed.skillFilter)) options.skill.push(parsed.skillFilter);
2972
+ }
2973
+ const prepared = await resolveGitRepoSkills(source, parsed, options, spinner, skillsDir, tempDir);
2974
+ const cleanupFn = async () => {
2975
+ await cleanupDir(tempDir);
2976
+ };
2977
+ await executeInstallFlow(prepared, options, spinner, cleanupFn);
2978
+ } catch (error) {
2979
+ if (error instanceof GitCloneError) {
2980
+ logger.error(import_picocolors.default.red("Failed to clone repository"));
2981
+ for (const line of error.message.split("\n")) logger.message(import_picocolors.default.dim(line));
2982
+ } else logger.error(error instanceof Error ? error.message : "Unknown error occurred");
2983
+ showInstallTip();
2984
+ logger.outro(import_picocolors.default.red("Installation failed"));
2985
+ process.exit(1);
2986
+ } finally {
2987
+ await cleanupDir(tempDir);
2988
+ }
2989
+ }
2990
+ async function cleanupDir(tempDir) {
2991
+ if (tempDir) try {
2992
+ await cleanupTempDir(tempDir);
2993
+ } catch {}
2994
+ }
2995
+ async function promptForFindSkills(options, targetAgents) {
2996
+ if (!process.stdin.isTTY) return;
2997
+ if (options?.yes) return;
2998
+ try {
2999
+ if (await isPromptDismissed("findSkillsPrompt")) return;
3000
+ if (await isSkillInstalled("find-skills", "claude-code", { global: true })) {
3001
+ await dismissPrompt("findSkillsPrompt");
3002
+ return;
3003
+ }
3004
+ logger.line();
3005
+ logger.message(import_picocolors.default.dim("One-time prompt - you won't be asked again if you dismiss."));
3006
+ const install = await ye({ message: `Install the ${import_picocolors.default.cyan("find-skills")} skill? It helps your agent discover and suggest skills.` });
3007
+ if (pD(install)) {
3008
+ await dismissPrompt("findSkillsPrompt");
3009
+ return;
3010
+ }
3011
+ if (install) {
3012
+ await dismissPrompt("findSkillsPrompt");
3013
+ const findSkillsAgents = targetAgents?.filter((a) => a !== "replit");
3014
+ if (!findSkillsAgents || findSkillsAgents.length === 0) return;
3015
+ logger.line();
3016
+ logger.step("Installing find-skills skill...");
3017
+ try {
3018
+ await runAdd(["vercel-labs/skills"], {
3019
+ skill: ["find-skills"],
3020
+ global: true,
3021
+ yes: true,
3022
+ agent: findSkillsAgents
3023
+ });
3024
+ } catch {
3025
+ logger.warning("Failed to install find-skills. You can try again with:");
3026
+ logger.message(import_picocolors.default.dim(" npx synk add vercel-labs/skills@find-skills -g -y --all"));
3027
+ }
3028
+ } else {
3029
+ await dismissPrompt("findSkillsPrompt");
3030
+ logger.message(import_picocolors.default.dim("You can install it later with: npx synk add vercel-labs/skills@find-skills"));
3031
+ }
3032
+ } catch {}
3033
+ }
3034
+ function parseAddOptions(args) {
3035
+ const options = {};
3036
+ const source = [];
3037
+ for (let i = 0; i < args.length; i++) {
3038
+ const arg = args[i];
3039
+ if (arg === "-g" || arg === "--global") options.global = true;
3040
+ else if (arg === "-y" || arg === "--yes") options.yes = true;
3041
+ else if (arg === "-l" || arg === "--list") options.list = true;
3042
+ else if (arg === "--all") options.all = true;
3043
+ else if (arg === "-a" || arg === "--agent") {
3044
+ options.agent = options.agent || [];
3045
+ i++;
3046
+ let nextArg = args[i];
3047
+ while (i < args.length && nextArg && !nextArg.startsWith("-")) {
3048
+ options.agent.push(nextArg);
3049
+ i++;
3050
+ nextArg = args[i];
3051
+ }
3052
+ i--;
3053
+ } else if (arg === "-s" || arg === "--skill") {
3054
+ options.skill = options.skill || [];
3055
+ i++;
3056
+ let nextArg = args[i];
3057
+ while (i < args.length && nextArg && !nextArg.startsWith("-")) {
3058
+ options.skill.push(nextArg);
3059
+ i++;
3060
+ nextArg = args[i];
3061
+ }
3062
+ i--;
3063
+ } else if (arg === "-t" || arg === "--type") {
3064
+ i++;
3065
+ const typeVal = args[i];
3066
+ if (typeVal === "skill" || typeVal === "agent" || typeVal === "prompt") options.type = typeVal;
3067
+ } else if (arg === "--full-depth") options.fullDepth = true;
3068
+ else if (arg && !arg.startsWith("-")) source.push(arg);
3069
+ }
3070
+ return {
3071
+ source,
3072
+ options
3073
+ };
3074
+ }
3075
+ const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
3076
+ async function searchSkillsAPI(query) {
3077
+ try {
3078
+ const url = `${SEARCH_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=10`;
3079
+ const res = await fetch(url);
3080
+ if (!res.ok) return [];
3081
+ return (await res.json()).skills.map((skill) => ({
3082
+ name: skill.name,
3083
+ slug: skill.id,
3084
+ source: skill.source || "",
3085
+ installs: skill.installs
3086
+ }));
3087
+ } catch {
3088
+ return [];
3089
+ }
3090
+ }
3091
+ const HIDE_CURSOR = "\x1B[?25l";
3092
+ const SHOW_CURSOR = "\x1B[?25h";
3093
+ const CLEAR_DOWN = "\x1B[J";
3094
+ const MOVE_UP = (n) => `\x1b[${n}A`;
3095
+ const MOVE_TO_COL = (n) => `\x1b[${n}G`;
3096
+ async function runSearchPrompt(initialQuery = "") {
3097
+ let results = [];
3098
+ let selectedIndex = 0;
3099
+ let query = initialQuery;
3100
+ let loading = false;
3101
+ let debounceTimer = null;
3102
+ let lastRenderedLines = 0;
3103
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
3104
+ readline.emitKeypressEvents(process.stdin);
3105
+ process.stdin.resume();
3106
+ process.stdout.write(HIDE_CURSOR);
3107
+ function render() {
3108
+ if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
3109
+ process.stdout.write(CLEAR_DOWN);
3110
+ const lines = [];
3111
+ const cursor = `${import_picocolors.default.bold("_")}`;
3112
+ lines.push(`Search skills: ${query}${cursor}`);
3113
+ lines.push("");
3114
+ if (!query || query.length < 2) lines.push(import_picocolors.default.dim("Start typing to search (min 2 chars)"));
3115
+ else if (results.length === 0 && loading) lines.push(import_picocolors.default.dim("Searching..."));
3116
+ else if (results.length === 0) lines.push(import_picocolors.default.dim("No skills found"));
3117
+ else {
3118
+ const visible = results.slice(0, 8);
3119
+ for (let i = 0; i < visible.length; i++) {
3120
+ const skill = visible[i];
3121
+ const isSelected = i === selectedIndex;
3122
+ const arrow = isSelected ? import_picocolors.default.bold(">") : " ";
3123
+ const name = isSelected ? import_picocolors.default.bold(skill.name) : skill.name;
3124
+ const source = skill.source ? ` ${import_picocolors.default.dim(skill.source)}` : "";
3125
+ const loadingIndicator = loading && i === 0 ? ` ${import_picocolors.default.dim("...")}` : "";
3126
+ lines.push(` ${arrow} ${name}${source}${loadingIndicator}`);
3127
+ }
3128
+ }
3129
+ lines.push("");
3130
+ lines.push(import_picocolors.default.dim("up/down navigate | enter select | esc cancel"));
3131
+ for (const line of lines) process.stdout.write(line + "\n");
3132
+ lastRenderedLines = lines.length;
3133
+ }
3134
+ function triggerSearch(q) {
3135
+ if (debounceTimer) {
3136
+ clearTimeout(debounceTimer);
3137
+ debounceTimer = null;
3138
+ }
3139
+ loading = false;
3140
+ if (!q || q.length < 2) {
3141
+ results = [];
3142
+ selectedIndex = 0;
3143
+ render();
3144
+ return;
3145
+ }
3146
+ loading = true;
3147
+ render();
3148
+ const debounceMs = Math.max(150, 350 - q.length * 50);
3149
+ debounceTimer = setTimeout(async () => {
3150
+ try {
3151
+ results = await searchSkillsAPI(q);
3152
+ selectedIndex = 0;
3153
+ } catch {
3154
+ results = [];
3155
+ } finally {
3156
+ loading = false;
3157
+ debounceTimer = null;
3158
+ render();
3159
+ }
3160
+ }, debounceMs);
3161
+ }
3162
+ if (initialQuery) triggerSearch(initialQuery);
3163
+ render();
3164
+ return new Promise((resolve) => {
3165
+ function cleanup() {
3166
+ process.stdin.removeListener("keypress", handleKeypress);
3167
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
3168
+ process.stdout.write(SHOW_CURSOR);
3169
+ process.stdin.pause();
3170
+ }
3171
+ function handleKeypress(_ch, key) {
3172
+ if (!key) return;
3173
+ if (key.name === "escape" || key.ctrl && key.name === "c") {
3174
+ cleanup();
3175
+ resolve(null);
3176
+ return;
3177
+ }
3178
+ if (key.name === "return") {
3179
+ cleanup();
3180
+ resolve(results[selectedIndex] || null);
3181
+ return;
3182
+ }
3183
+ if (key.name === "up") {
3184
+ selectedIndex = Math.max(0, selectedIndex - 1);
3185
+ render();
3186
+ return;
3187
+ }
3188
+ if (key.name === "down") {
3189
+ selectedIndex = Math.min(Math.max(0, results.length - 1), selectedIndex + 1);
3190
+ render();
3191
+ return;
3192
+ }
3193
+ if (key.name === "backspace") {
3194
+ if (query.length > 0) {
3195
+ query = query.slice(0, -1);
3196
+ triggerSearch(query);
3197
+ }
3198
+ return;
3199
+ }
3200
+ if (key.sequence && !key.ctrl && !key.meta && key.sequence.length === 1) {
3201
+ const char = key.sequence;
3202
+ if (char >= " " && char <= "~") {
3203
+ query += char;
3204
+ triggerSearch(query);
3205
+ }
3206
+ }
3207
+ }
3208
+ process.stdin.on("keypress", handleKeypress);
3209
+ });
3210
+ }
3211
+ function getOwnerRepoFromString(pkg) {
3212
+ const atIndex = pkg.lastIndexOf("@");
3213
+ const match = (atIndex > 0 ? pkg.slice(0, atIndex) : pkg).match(/^([^/]+)\/([^/]+)$/);
3214
+ if (match) return {
3215
+ owner: match[1],
3216
+ repo: match[2]
3217
+ };
3218
+ return null;
3219
+ }
3220
+ async function isRepoPublic(owner, repo) {
3221
+ return await isRepoPrivate(owner, repo) === false;
3222
+ }
3223
+ async function runFind(args) {
3224
+ const query = args.join(" ");
3225
+ const isNonInteractive = !process.stdin.isTTY;
3226
+ const agentTip = `${import_picocolors.default.dim("Tip: if running in a coding agent, follow these steps:")}
3227
+ ${import_picocolors.default.dim(" 1) npx synk find [query]")}
3228
+ ${import_picocolors.default.dim(" 2) npx synk add <owner/repo@skill>")}`;
3229
+ if (query) {
3230
+ const results = await searchSkillsAPI(query);
3231
+ track({
3232
+ event: "find",
3233
+ query,
3234
+ resultCount: String(results.length)
3235
+ });
3236
+ if (results.length === 0) {
3237
+ logger.dim(`No skills found for "${query}"`);
3238
+ return;
3239
+ }
3240
+ logger.log(`${import_picocolors.default.dim("Install with")} npx synk add <owner/repo@skill>`);
3241
+ logger.line();
3242
+ for (const skill of results.slice(0, 6)) {
3243
+ const pkg = skill.source || skill.slug;
3244
+ logger.log(`${pkg}@${skill.name}`);
3245
+ logger.dim(`\u2514 https://skills.sh/${skill.slug}`);
3246
+ logger.line();
3247
+ }
3248
+ return;
3249
+ }
3250
+ if (isNonInteractive) {
3251
+ logger.log(agentTip);
3252
+ logger.line();
3253
+ }
3254
+ const selected = await runSearchPrompt();
3255
+ track({
3256
+ event: "find",
3257
+ query: "",
3258
+ resultCount: selected ? "1" : "0",
3259
+ interactive: "1"
3260
+ });
3261
+ if (!selected) {
3262
+ logger.dim("Search cancelled");
3263
+ logger.line();
3264
+ return;
3265
+ }
3266
+ const pkg = selected.source || selected.slug;
3267
+ const skillName = selected.name;
3268
+ logger.line();
3269
+ logger.log(`Installing ${import_picocolors.default.bold(skillName)} from ${import_picocolors.default.dim(pkg)}...`);
3270
+ logger.line();
3271
+ const { source, options } = parseAddOptions([
3272
+ pkg,
3273
+ "--skill",
3274
+ skillName
3275
+ ]);
3276
+ await runAdd(source, options);
3277
+ logger.line();
3278
+ const info = getOwnerRepoFromString(pkg);
3279
+ if (info && await isRepoPublic(info.owner, info.repo)) logger.log(`${import_picocolors.default.dim("View the skill at")} https://skills.sh/${selected.slug}`);
3280
+ else logger.log(`${import_picocolors.default.dim("Discover more skills at")} https://skills.sh`);
3281
+ logger.line();
3282
+ }
3283
+ function shortenPath(fullPath, cwd) {
3284
+ const home = homedir();
3285
+ if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
3286
+ if (fullPath.startsWith(cwd)) return "." + fullPath.slice(cwd.length);
3287
+ return fullPath;
3288
+ }
3289
+ function formatList(items, maxShow = 5) {
3290
+ if (items.length <= maxShow) return items.join(", ");
3291
+ const shown = items.slice(0, maxShow);
3292
+ const remaining = items.length - maxShow;
3293
+ return `${shown.join(", ")} +${remaining} more`;
3294
+ }
3295
+ function parseListOptions(args) {
3296
+ const options = {};
3297
+ for (let i = 0; i < args.length; i++) {
3298
+ const arg = args[i];
3299
+ if (arg === "-g" || arg === "--global") options.global = true;
3300
+ else if (arg === "-a" || arg === "--agent") {
3301
+ options.agent = options.agent || [];
3302
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) options.agent.push(args[++i]);
3303
+ } else if (arg === "-t" || arg === "--type") {
3304
+ i++;
3305
+ const typeVal = args[i];
3306
+ if (typeVal === "skill" || typeVal === "agent" || typeVal === "prompt") options.type = typeVal;
3307
+ }
3308
+ }
3309
+ return options;
3310
+ }
3311
+ async function runList(args) {
3312
+ const options = parseListOptions(args);
3313
+ const scope = options.global === true ? true : false;
3314
+ let agentFilter;
3315
+ if (options.agent && options.agent.length > 0) {
3316
+ const validAgents = Object.keys(agents);
3317
+ const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
3318
+ if (invalidAgents.length > 0) {
3319
+ logger.warning(`Invalid agents: ${invalidAgents.join(", ")}`);
3320
+ logger.dim(`Valid agents: ${validAgents.join(", ")}`);
3321
+ process.exit(1);
3322
+ }
3323
+ agentFilter = options.agent;
3324
+ }
3325
+ const installedSkills = await listInstalledSkills({
3326
+ global: scope,
3327
+ agentFilter
3328
+ });
3329
+ const filteredSkills = options.type ? installedSkills.filter((s) => (s.cognitiveType || "skill") === options.type) : installedSkills;
3330
+ const cwd = process.cwd();
3331
+ const scopeLabel = scope ? "Global" : "Project";
3332
+ const typeLabel = options.type ? ` ${options.type}s` : "";
3333
+ if (filteredSkills.length === 0) {
3334
+ logger.dim(`No ${scopeLabel.toLowerCase()}${typeLabel} found.`);
3335
+ if (scope) logger.dim("Try listing project cognitives without -g");
3336
+ else logger.dim("Try listing global cognitives with -g");
3337
+ return;
3338
+ }
3339
+ function getTypeLabel(skill) {
3340
+ const ct = skill.cognitiveType || "skill";
3341
+ if (ct === "agent") return ` ${import_picocolors.default.dim("[AGENT]")}`;
3342
+ if (ct === "prompt") return ` ${import_picocolors.default.dim("[PROMPT]")}`;
3343
+ return "";
3344
+ }
3345
+ function printSkill(skill) {
3346
+ const shortPath = shortenPath(skill.canonicalPath, cwd);
3347
+ const agentNames = skill.agents.map((a) => agents[a].displayName);
3348
+ const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : import_picocolors.default.yellow("not linked");
3349
+ logger.log(`${import_picocolors.default.cyan(skill.name)}${getTypeLabel(skill)} ${import_picocolors.default.dim(shortPath)}`);
3350
+ logger.log(` ${import_picocolors.default.dim("Agents:")} ${agentInfo}`);
3351
+ }
3352
+ logger.bold(`${scopeLabel}${typeLabel ? typeLabel.charAt(0).toUpperCase() + typeLabel.slice(1) : " Cognitives"}`);
3353
+ logger.line();
3354
+ for (const skill of filteredSkills) printSkill(skill);
3355
+ logger.line();
3356
+ }
3357
+ async function removeCommand(skillNames, options) {
3358
+ const isGlobal = options.global ?? false;
3359
+ const cwd = process.cwd();
3360
+ const spinner = logger.spinner("Scanning for installed skills...");
3361
+ const skillNamesSet = /* @__PURE__ */ new Set();
3362
+ const scanDir = async (dir) => {
3363
+ try {
3364
+ const entries = await readdir(dir, { withFileTypes: true });
3365
+ for (const entry of entries) if (entry.isDirectory()) skillNamesSet.add(entry.name);
3366
+ } catch (err) {
3367
+ if (err instanceof Error && err.code !== "ENOENT") logger.warning(`Could not scan directory ${dir}: ${err.message}`);
3368
+ }
3369
+ };
3370
+ const typesToScan = options.type ? [options.type] : [
3371
+ "skill",
3372
+ "agent",
3373
+ "prompt"
3374
+ ];
3375
+ for (const cogType of typesToScan) if (isGlobal) {
3376
+ await scanDir(getCanonicalDir(cogType, true, cwd));
3377
+ for (const agent of Object.values(agents)) {
3378
+ const dir = cogType === "skill" ? agent.globalSkillsDir : cogType === "agent" ? agent.globalAgentsDir : agent.globalPromptsDir;
3379
+ if (dir !== void 0) await scanDir(dir);
3380
+ }
3381
+ } else {
3382
+ await scanDir(getCanonicalDir(cogType, false, cwd));
3383
+ for (const agent of Object.values(agents)) await scanDir(join(cwd, cogType === "skill" ? agent.skillsDir : cogType === "agent" ? agent.agentsDir : agent.promptsDir));
3384
+ }
3385
+ const installedSkills = Array.from(skillNamesSet).sort();
3386
+ spinner.succeed(`Found ${installedSkills.length} unique installed skill(s)`);
3387
+ if (installedSkills.length === 0) {
3388
+ logger.outro(import_picocolors.default.yellow("No skills found to remove."));
3389
+ return;
3390
+ }
3391
+ if (options.agent && options.agent.length > 0) {
3392
+ const validAgents = Object.keys(agents);
3393
+ const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
3394
+ if (invalidAgents.length > 0) {
3395
+ logger.error(`Invalid agents: ${invalidAgents.join(", ")}`);
3396
+ logger.info(`Valid agents: ${validAgents.join(", ")}`);
3397
+ process.exit(1);
3398
+ }
3399
+ }
3400
+ let selectedSkills = [];
3401
+ if (options.all) selectedSkills = installedSkills;
3402
+ else if (skillNames.length > 0) {
3403
+ selectedSkills = installedSkills.filter((s) => skillNames.some((name) => name.toLowerCase() === s.toLowerCase()));
3404
+ if (selectedSkills.length === 0) {
3405
+ logger.error(`No matching skills found for: ${skillNames.join(", ")}`);
3406
+ return;
3407
+ }
3408
+ } else {
3409
+ const choices = installedSkills.map((s) => ({
3410
+ value: s,
3411
+ label: s
3412
+ }));
3413
+ const selected = await fe({
3414
+ message: `Select skills to remove ${import_picocolors.default.dim("(space to toggle)")}`,
3415
+ options: choices,
3416
+ required: true
3417
+ });
3418
+ if (pD(selected)) {
3419
+ logger.cancel("Removal cancelled");
3420
+ process.exit(0);
3421
+ }
3422
+ selectedSkills = selected;
3423
+ }
3424
+ let targetAgents;
3425
+ if (options.agent && options.agent.length > 0) targetAgents = options.agent;
3426
+ else {
3427
+ spinner.start("Detecting installed agents...");
3428
+ targetAgents = await detectInstalledAgents();
3429
+ if (targetAgents.length === 0) targetAgents = Object.keys(agents);
3430
+ spinner.succeed(`Targeting ${targetAgents.length} installed agent(s)`);
3431
+ }
3432
+ if (!options.yes) {
3433
+ logger.line();
3434
+ logger.info("Skills to remove:");
3435
+ for (const skill of selectedSkills) logger.message(`${import_picocolors.default.red("•")} ${skill}`);
3436
+ logger.line();
3437
+ const confirmed = await ye({ message: `Are you sure you want to uninstall ${selectedSkills.length} skill(s)?` });
3438
+ if (pD(confirmed) || !confirmed) {
3439
+ logger.cancel("Removal cancelled");
3440
+ process.exit(0);
3441
+ }
3442
+ }
3443
+ spinner.start("Removing skills...");
3444
+ const results = [];
3445
+ for (const skillName of selectedSkills) try {
3446
+ for (const agentKey of targetAgents) {
3447
+ const agent = agents[agentKey];
3448
+ const skillPath = getInstallPath(skillName, agentKey, {
3449
+ global: isGlobal,
3450
+ cwd
3451
+ });
3452
+ try {
3453
+ if (await lstat(skillPath).catch(() => null)) await rm(skillPath, {
3454
+ recursive: true,
3455
+ force: true
3456
+ });
3457
+ } catch (err) {
3458
+ logger.warning(`Could not remove skill from ${agent.displayName}: ${err instanceof Error ? err.message : String(err)}`);
3459
+ }
3460
+ }
3461
+ for (const cogType of typesToScan) await rm(getCanonicalPath(skillName, {
3462
+ global: isGlobal,
3463
+ cwd,
3464
+ cognitiveType: cogType
3465
+ }), {
3466
+ recursive: true,
3467
+ force: true
3468
+ });
3469
+ const lockEntry = isGlobal ? await getSkillFromLock(skillName) : null;
3470
+ const effectiveSource = lockEntry?.source || "local";
3471
+ const effectiveSourceType = lockEntry?.sourceType || "local";
3472
+ if (isGlobal) await removeSkillFromLock(skillName);
3473
+ results.push({
3474
+ skill: skillName,
3475
+ success: true,
3476
+ source: effectiveSource,
3477
+ sourceType: effectiveSourceType
3478
+ });
3479
+ } catch (err) {
3480
+ results.push({
3481
+ skill: skillName,
3482
+ success: false,
3483
+ error: err instanceof Error ? err.message : String(err)
3484
+ });
3485
+ }
3486
+ spinner.succeed("Removal process complete");
3487
+ const successful = results.filter((r) => r.success);
3488
+ const failed = results.filter((r) => !r.success);
3489
+ if (successful.length > 0) {
3490
+ const bySource = /* @__PURE__ */ new Map();
3491
+ for (const r of successful) {
3492
+ const source = r.source || "local";
3493
+ const existing = bySource.get(source) || { skills: [] };
3494
+ existing.skills.push(r.skill);
3495
+ existing.sourceType = r.sourceType;
3496
+ bySource.set(source, existing);
3497
+ }
3498
+ for (const [source, data] of bySource) track({
3499
+ event: "remove",
3500
+ source,
3501
+ skills: data.skills.join(","),
3502
+ agents: targetAgents.join(","),
3503
+ ...isGlobal && { global: "1" },
3504
+ sourceType: data.sourceType
3505
+ });
3506
+ }
3507
+ if (successful.length > 0) logger.success(import_picocolors.default.green(`Successfully removed ${successful.length} skill(s)`));
3508
+ if (failed.length > 0) {
3509
+ logger.error(import_picocolors.default.red(`Failed to remove ${failed.length} skill(s)`));
3510
+ for (const r of failed) logger.message(`${import_picocolors.default.red("✗")} ${r.skill}: ${r.error}`);
3511
+ }
3512
+ logger.line();
3513
+ logger.outro(import_picocolors.default.green("Done!"));
3514
+ }
3515
+ function parseRemoveOptions(args) {
3516
+ const options = {};
3517
+ const skills = [];
3518
+ for (let i = 0; i < args.length; i++) {
3519
+ const arg = args[i];
3520
+ if (arg === "-g" || arg === "--global") options.global = true;
3521
+ else if (arg === "-y" || arg === "--yes") options.yes = true;
3522
+ else if (arg === "--all") options.all = true;
3523
+ else if (arg === "-a" || arg === "--agent") {
3524
+ options.agent = options.agent || [];
3525
+ i++;
3526
+ let nextArg = args[i];
3527
+ while (i < args.length && nextArg && !nextArg.startsWith("-")) {
3528
+ options.agent.push(nextArg);
3529
+ i++;
3530
+ nextArg = args[i];
3531
+ }
3532
+ i--;
3533
+ } else if (arg === "-t" || arg === "--type") {
3534
+ i++;
3535
+ const typeVal = args[i];
3536
+ if (typeVal === "skill" || typeVal === "agent" || typeVal === "prompt") options.type = typeVal;
3537
+ } else if (arg && !arg.startsWith("-")) skills.push(arg);
3538
+ }
3539
+ return {
3540
+ skills,
3541
+ options
3542
+ };
3543
+ }
3544
+ function runInit(args) {
3545
+ const cwd = process.cwd();
3546
+ let cognitiveType = "skill";
3547
+ const filteredArgs = [];
3548
+ for (let i = 0; i < args.length; i++) {
3549
+ const arg = args[i];
3550
+ if ((arg === "-t" || arg === "--type") && i + 1 < args.length) {
3551
+ const typeVal = args[i + 1];
3552
+ if (typeVal === "skill" || typeVal === "agent" || typeVal === "prompt") cognitiveType = typeVal;
3553
+ i++;
3554
+ } else if (arg && !arg.startsWith("-")) filteredArgs.push(arg);
3555
+ }
3556
+ const itemName = filteredArgs[0] || basename(cwd);
3557
+ const hasName = filteredArgs[0] !== void 0;
3558
+ const fileName = {
3559
+ skill: "SKILL.md",
3560
+ agent: "AGENT.md",
3561
+ prompt: "PROMPT.md"
3562
+ }[cognitiveType];
3563
+ const itemDir = hasName ? join(cwd, itemName) : cwd;
3564
+ const itemFile = join(itemDir, fileName);
3565
+ const displayPath = hasName ? `${itemName}/${fileName}` : fileName;
3566
+ if (existsSync(itemFile)) {
3567
+ logger.log(`${cognitiveType} already exists at ${import_picocolors.default.dim(displayPath)}`);
3568
+ return;
3569
+ }
3570
+ if (hasName) mkdirSync(itemDir, { recursive: true });
3571
+ let content;
3572
+ if (cognitiveType === "agent") content = `---
3573
+ name: ${itemName}
3574
+ description: A brief description of this agent
3575
+ ---
3576
+
3577
+ # ${itemName}
3578
+
3579
+ Agent instructions here.
3580
+
3581
+ ## Role
3582
+
3583
+ Describe the agent's role and capabilities.
3584
+
3585
+ ## Instructions
3586
+
3587
+ 1. First step
3588
+ 2. Second step
3589
+ 3. Additional steps as needed
3590
+ `;
3591
+ else if (cognitiveType === "prompt") content = `---
3592
+ name: ${itemName}
3593
+ description: A brief description of this prompt
3594
+ ---
3595
+
3596
+ # ${itemName}
3597
+
3598
+ Prompt template content here.
3599
+
3600
+ ## Context
3601
+
3602
+ Describe when this prompt should be used.
3603
+
3604
+ ## Template
3605
+
3606
+ Your prompt template goes here.
3607
+ `;
3608
+ else content = `---
3609
+ name: ${itemName}
3610
+ description: A brief description of what this skill does
3611
+ ---
3612
+
3613
+ # ${itemName}
3614
+
3615
+ Instructions for the agent to follow when this skill is activated.
3616
+
3617
+ ## When to use
3618
+
3619
+ Describe when this skill should be used.
3620
+
3621
+ ## Instructions
3622
+
3623
+ 1. First step
3624
+ 2. Second step
3625
+ 3. Additional steps as needed
3626
+ `;
3627
+ writeFileSync(itemFile, content);
3628
+ logger.log(`Initialized ${cognitiveType}: ${import_picocolors.default.dim(itemName)}`);
3629
+ logger.line();
3630
+ logger.dim("Created:");
3631
+ logger.log(` ${displayPath}`);
3632
+ logger.line();
3633
+ logger.dim("Next steps:");
3634
+ logger.log(` 1. Edit ${import_picocolors.default.cyan(displayPath)} to define your ${cognitiveType} instructions`);
3635
+ logger.log(` 2. Update the ${import_picocolors.default.cyan("name")} and ${import_picocolors.default.cyan("description")} in the frontmatter`);
3636
+ logger.line();
3637
+ logger.dim("Publishing:");
3638
+ logger.log(` ${import_picocolors.default.dim("GitHub:")} Push to a repo, then ${import_picocolors.default.cyan(`npx synk add <owner>/<repo>`)}`);
3639
+ logger.log(` ${import_picocolors.default.dim("URL:")} Host the file, then ${import_picocolors.default.cyan(`npx synk add https://example.com/${displayPath}`)}`);
3640
+ logger.line();
3641
+ }
3642
+ const AGENTS_DIR$1 = ".agents";
3643
+ const LOCK_FILE$1 = ".skill-lock.json";
3644
+ const CURRENT_LOCK_VERSION$1 = 4;
3645
+ function getSkillLockPath$1() {
3646
+ return join(homedir(), AGENTS_DIR$1, LOCK_FILE$1);
3647
+ }
3648
+ function readSkillLock$1() {
3649
+ const lockPath = getSkillLockPath$1();
3650
+ try {
3651
+ const content = readFileSync(lockPath, "utf-8");
3652
+ const parsed = JSON.parse(content);
3653
+ if (typeof parsed.version !== "number" || !parsed.skills) return {
3654
+ version: CURRENT_LOCK_VERSION$1,
3655
+ skills: {}
3656
+ };
3657
+ if (parsed.version < CURRENT_LOCK_VERSION$1) return {
3658
+ version: CURRENT_LOCK_VERSION$1,
3659
+ skills: {}
3660
+ };
3661
+ return parsed;
3662
+ } catch {
3663
+ return {
3664
+ version: CURRENT_LOCK_VERSION$1,
3665
+ skills: {}
3666
+ };
3667
+ }
3668
+ }
3669
+ async function runCheck(args = []) {
3670
+ logger.log(`Checking for skill updates...`);
3671
+ logger.line();
3672
+ const lock = readSkillLock$1();
3673
+ const skillNames = Object.keys(lock.skills);
3674
+ if (skillNames.length === 0) {
3675
+ logger.dim("No skills tracked in lock file.");
3676
+ logger.log(`${import_picocolors.default.dim("Install skills with")} npx synk add <package>`);
3677
+ return;
3678
+ }
3679
+ const token = getGitHubToken();
3680
+ const skillsBySource = /* @__PURE__ */ new Map();
3681
+ let skippedCount = 0;
3682
+ for (const skillName of skillNames) {
3683
+ const entry = lock.skills[skillName];
3684
+ if (!entry) continue;
3685
+ if (entry.sourceType !== "github" || !entry.skillFolderHash || !entry.skillPath) {
3686
+ skippedCount++;
3687
+ continue;
3688
+ }
3689
+ const existing = skillsBySource.get(entry.source) || [];
3690
+ existing.push({
3691
+ name: skillName,
3692
+ entry
3693
+ });
3694
+ skillsBySource.set(entry.source, existing);
3695
+ }
3696
+ const totalSkills = skillNames.length - skippedCount;
3697
+ if (totalSkills === 0) {
3698
+ logger.dim("No GitHub skills to check.");
3699
+ return;
3700
+ }
3701
+ logger.dim(`Checking ${totalSkills} skill(s) for updates...`);
3702
+ const updates = [];
3703
+ const errors = [];
3704
+ for (const [source, skills] of skillsBySource) for (const { name, entry } of skills) try {
3705
+ const latestHash = await fetchSkillFolderHash(source, entry.skillPath, token);
3706
+ if (!latestHash) {
3707
+ errors.push({
3708
+ name,
3709
+ source,
3710
+ error: "Could not fetch from GitHub"
3711
+ });
3712
+ continue;
3713
+ }
3714
+ if (latestHash !== entry.skillFolderHash) updates.push({
3715
+ name,
3716
+ source
3717
+ });
3718
+ } catch (err) {
3719
+ errors.push({
3720
+ name,
3721
+ source,
3722
+ error: err instanceof Error ? err.message : "Unknown error"
3723
+ });
3724
+ }
3725
+ logger.line();
3726
+ if (updates.length === 0) logger.success("All skills are up to date");
3727
+ else {
3728
+ logger.log(`${updates.length} update(s) available:`);
3729
+ logger.line();
3730
+ for (const update of updates) {
3731
+ logger.log(` ${import_picocolors.default.cyan("↑")} ${update.name}`);
3732
+ logger.dim(` source: ${update.source}`);
3733
+ }
3734
+ logger.line();
3735
+ logger.log(`${import_picocolors.default.dim("Run")} npx synk update ${import_picocolors.default.dim("to update all skills")}`);
3736
+ }
3737
+ if (errors.length > 0) {
3738
+ logger.line();
3739
+ logger.dim(`Could not check ${errors.length} skill(s) (may need reinstall)`);
3740
+ }
3741
+ track({
3742
+ event: "check",
3743
+ skillCount: String(totalSkills),
3744
+ updatesAvailable: String(updates.length)
3745
+ });
3746
+ logger.line();
3747
+ }
3748
+ const AGENTS_DIR = ".agents";
3749
+ const LOCK_FILE = ".skill-lock.json";
3750
+ const CURRENT_LOCK_VERSION = 4;
3751
+ function getSkillLockPath() {
3752
+ return join(homedir(), AGENTS_DIR, LOCK_FILE);
3753
+ }
3754
+ function readSkillLock() {
3755
+ const lockPath = getSkillLockPath();
3756
+ try {
3757
+ const content = readFileSync(lockPath, "utf-8");
3758
+ const parsed = JSON.parse(content);
3759
+ if (typeof parsed.version !== "number" || !parsed.skills) return {
3760
+ version: CURRENT_LOCK_VERSION,
3761
+ skills: {}
3762
+ };
3763
+ if (parsed.version < CURRENT_LOCK_VERSION) return {
3764
+ version: CURRENT_LOCK_VERSION,
3765
+ skills: {}
3766
+ };
3767
+ return parsed;
3768
+ } catch {
3769
+ return {
3770
+ version: CURRENT_LOCK_VERSION,
3771
+ skills: {}
3772
+ };
3773
+ }
3774
+ }
3775
+ async function runUpdate() {
3776
+ logger.log("Checking for skill updates...");
3777
+ logger.line();
3778
+ const lock = readSkillLock();
3779
+ const skillNames = Object.keys(lock.skills);
3780
+ if (skillNames.length === 0) {
3781
+ logger.dim("No skills tracked in lock file.");
3782
+ logger.log(`${import_picocolors.default.dim("Install skills with")} npx synk add <package>`);
3783
+ return;
3784
+ }
3785
+ const token = getGitHubToken();
3786
+ const updates = [];
3787
+ let checkedCount = 0;
3788
+ for (const skillName of skillNames) {
3789
+ const entry = lock.skills[skillName];
3790
+ if (!entry) continue;
3791
+ if (entry.sourceType !== "github" || !entry.skillFolderHash || !entry.skillPath) continue;
3792
+ checkedCount++;
3793
+ try {
3794
+ const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
3795
+ if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
3796
+ name: skillName,
3797
+ source: entry.source,
3798
+ entry
3799
+ });
3800
+ } catch {}
3801
+ }
3802
+ if (checkedCount === 0) {
3803
+ logger.dim("No skills to check.");
3804
+ return;
3805
+ }
3806
+ if (updates.length === 0) {
3807
+ logger.success("All skills are up to date");
3808
+ logger.line();
3809
+ return;
3810
+ }
3811
+ logger.log(`Found ${updates.length} update(s)`);
3812
+ logger.line();
3813
+ let successCount = 0;
3814
+ let failCount = 0;
3815
+ for (const update of updates) {
3816
+ logger.log(`Updating ${update.name}...`);
3817
+ let installUrl = update.entry.sourceUrl;
3818
+ if (update.entry.skillPath) {
3819
+ let skillFolder = update.entry.skillPath;
3820
+ if (skillFolder.endsWith("/SKILL.md")) skillFolder = skillFolder.slice(0, -9);
3821
+ else if (skillFolder.endsWith("SKILL.md")) skillFolder = skillFolder.slice(0, -8);
3822
+ if (skillFolder.endsWith("/")) skillFolder = skillFolder.slice(0, -1);
3823
+ installUrl = update.entry.sourceUrl.replace(/\.git$/, "").replace(/\/$/, "");
3824
+ installUrl = `${installUrl}/tree/main/${skillFolder}`;
3825
+ }
3826
+ if (spawnSync("npx", [
3827
+ "-y",
3828
+ "skills",
3829
+ "add",
3830
+ installUrl,
3831
+ "-g",
3832
+ "-y"
3833
+ ], { stdio: [
3834
+ "inherit",
3835
+ "pipe",
3836
+ "pipe"
3837
+ ] }).status === 0) {
3838
+ successCount++;
3839
+ logger.success(`Updated ${update.name}`);
3840
+ } else {
3841
+ failCount++;
3842
+ logger.dim(`\u2717 Failed to update ${update.name}`);
3843
+ }
3844
+ }
3845
+ logger.line();
3846
+ if (successCount > 0) logger.success(`Updated ${successCount} skill(s)`);
3847
+ if (failCount > 0) logger.dim(`Failed to update ${failCount} skill(s)`);
3848
+ track({
3849
+ event: "update",
3850
+ skillCount: String(updates.length),
3851
+ successCount: String(successCount),
3852
+ failCount: String(failCount)
3853
+ });
3854
+ logger.line();
3855
+ }
3856
+ const LOGO_LINES = [
3857
+ "███████╗██╗ ██╗███╗ ██╗██╗ ██╗",
3858
+ "██╔════╝╚██╗ ██╔╝████╗ ██║██║ ██╔╝",
3859
+ "███████╗ ╚████╔╝ ██╔██╗ ██║█████╔╝ ",
3860
+ "╚════██║ ╚██╔╝ ██║╚██╗██║██╔═██╗ ",
3861
+ "███████║ ██║ ██║ ╚████║██║ ██╗",
3862
+ "╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝"
3863
+ ];
3864
+ const GRAYS = [
3865
+ "\x1B[38;5;250m",
3866
+ "\x1B[38;5;248m",
3867
+ "\x1B[38;5;245m",
3868
+ "\x1B[38;5;243m",
3869
+ "\x1B[38;5;240m",
3870
+ "\x1B[38;5;238m"
3871
+ ];
3872
+ function showLogo() {
3873
+ logger.line();
3874
+ LOGO_LINES.forEach((line, i) => {
3875
+ logger.gradient(line, GRAYS[i]);
3876
+ });
3877
+ }
3878
+ function showBanner() {
3879
+ showLogo();
3880
+ logger.line();
3881
+ logger.dim("The open cognitive ecosystem for AI agents");
3882
+ logger.line();
3883
+ logger.command("npx synk add <package>", "Install skills, agents, or prompts");
3884
+ logger.command("npx synk list", "List installed cognitives");
3885
+ logger.command("npx synk find [query]", "Search for skills");
3886
+ logger.command("npx synk check", "Check for updates");
3887
+ logger.command("npx synk update", "Update all cognitives");
3888
+ logger.command("npx synk remove", "Remove installed cognitives");
3889
+ logger.command("npx synk init [name]", "Create a new skill, agent, or prompt");
3890
+ logger.line();
3891
+ logger.log(`${import_picocolors.default.dim("try:")} npx synk add vercel-labs/agent-skills`);
3892
+ logger.line();
3893
+ }
3894
+ function showHelp() {
3895
+ logger.log(`
3896
+ ${import_picocolors.default.bold("Usage:")} synk <command> [options]
3897
+
3898
+ ${import_picocolors.default.bold("Commands:")}
3899
+ add <package> Add skills, agents, or prompts from a package
3900
+ e.g. vercel-labs/agent-skills
3901
+ https://github.com/vercel-labs/agent-skills
3902
+ remove [names] Remove installed cognitives
3903
+ list, ls List installed cognitives
3904
+ find [query] Search for skills interactively
3905
+ init [name] Initialize a cognitive (creates SKILL.md, AGENT.md, or PROMPT.md)
3906
+ check Check for available updates
3907
+ update Update all cognitives to latest versions
3908
+
3909
+ ${import_picocolors.default.bold("Add Options:")}
3910
+ -g, --global Install globally (user-level) instead of project-level
3911
+ -a, --agent <agents> Specify agents to install to (use '*' for all agents)
3912
+ -s, --skill <skills> Specify skill names to install (use '*' for all skills)
3913
+ -t, --type <type> Filter by cognitive type: skill, agent, or prompt
3914
+ -l, --list List available cognitives in the repository without installing
3915
+ -y, --yes Skip confirmation prompts
3916
+ --all Shorthand for --skill '*' --agent '*' -y
3917
+ --full-depth Search all subdirectories even when a root SKILL.md exists
3918
+
3919
+ ${import_picocolors.default.bold("Remove Options:")}
3920
+ -g, --global Remove from global scope
3921
+ -a, --agent <agents> Remove from specific agents (use '*' for all agents)
3922
+ -s, --skill <skills> Specify skills to remove (use '*' for all skills)
3923
+ -t, --type <type> Filter by cognitive type: skill, agent, or prompt
3924
+ -y, --yes Skip confirmation prompts
3925
+ --all Shorthand for --skill '*' --agent '*' -y
3926
+
3927
+ ${import_picocolors.default.bold("List Options:")}
3928
+ -g, --global List global cognitives (default: project)
3929
+ -a, --agent <agents> Filter by specific agents
3930
+ -t, --type <type> Filter by cognitive type: skill, agent, or prompt
3931
+
3932
+ ${import_picocolors.default.bold("Init Options:")}
3933
+ -t, --type <type> Type to create: skill (default), agent, or prompt
3934
+
3935
+ ${import_picocolors.default.bold("Options:")}
3936
+ --help, -h Show this help message
3937
+ --version, -v Show version number
3938
+
3939
+ ${import_picocolors.default.bold("Examples:")}
3940
+ ${import_picocolors.default.dim("$")} synk add vercel-labs/agent-skills
3941
+ ${import_picocolors.default.dim("$")} synk add vercel-labs/agent-skills -g
3942
+ ${import_picocolors.default.dim("$")} synk add vercel-labs/agent-skills --type agent
3943
+ ${import_picocolors.default.dim("$")} synk add vercel-labs/agent-skills --agent claude-code cursor
3944
+ ${import_picocolors.default.dim("$")} synk add vercel-labs/agent-skills --skill pr-review commit
3945
+ ${import_picocolors.default.dim("$")} synk remove ${import_picocolors.default.dim("# interactive remove")}
3946
+ ${import_picocolors.default.dim("$")} synk remove web-design ${import_picocolors.default.dim("# remove by name")}
3947
+ ${import_picocolors.default.dim("$")} synk rm --global frontend-design
3948
+ ${import_picocolors.default.dim("$")} synk list ${import_picocolors.default.dim("# list all installed cognitives")}
3949
+ ${import_picocolors.default.dim("$")} synk ls -g ${import_picocolors.default.dim("# list global cognitives only")}
3950
+ ${import_picocolors.default.dim("$")} synk ls --type agent ${import_picocolors.default.dim("# list agents only")}
3951
+ ${import_picocolors.default.dim("$")} synk find ${import_picocolors.default.dim("# interactive search")}
3952
+ ${import_picocolors.default.dim("$")} synk find typescript ${import_picocolors.default.dim("# search by keyword")}
3953
+ ${import_picocolors.default.dim("$")} synk init my-skill
3954
+ ${import_picocolors.default.dim("$")} synk init --type agent my-agent
3955
+ ${import_picocolors.default.dim("$")} synk init --type prompt my-prompt
3956
+ ${import_picocolors.default.dim("$")} synk check
3957
+ ${import_picocolors.default.dim("$")} synk update
3958
+ `);
3959
+ }
3960
+ function showRemoveHelp() {
3961
+ logger.log(`
3962
+ ${import_picocolors.default.bold("Usage:")} synk remove [names...] [options]
3963
+
3964
+ ${import_picocolors.default.bold("Description:")}
3965
+ Remove installed cognitives from agents. If no names are provided,
3966
+ an interactive selection menu will be shown.
3967
+
3968
+ ${import_picocolors.default.bold("Arguments:")}
3969
+ names Optional names to remove (space-separated)
3970
+
3971
+ ${import_picocolors.default.bold("Options:")}
3972
+ -g, --global Remove from global scope (~/) instead of project scope
3973
+ -a, --agent Remove from specific agents (use '*' for all agents)
3974
+ -s, --skill Specify skills to remove (use '*' for all skills)
3975
+ -t, --type Filter by cognitive type: skill, agent, or prompt
3976
+ -y, --yes Skip confirmation prompts
3977
+ --all Shorthand for --skill '*' --agent '*' -y
3978
+
3979
+ ${import_picocolors.default.bold("Examples:")}
3980
+ ${import_picocolors.default.dim("$")} synk remove ${import_picocolors.default.dim("# interactive selection")}
3981
+ ${import_picocolors.default.dim("$")} synk remove my-skill ${import_picocolors.default.dim("# remove specific skill")}
3982
+ ${import_picocolors.default.dim("$")} synk remove skill1 skill2 -y ${import_picocolors.default.dim("# remove multiple")}
3983
+ ${import_picocolors.default.dim("$")} synk remove --global my-agent ${import_picocolors.default.dim("# remove from global scope")}
3984
+ ${import_picocolors.default.dim("$")} synk rm --agent claude-code my-skill ${import_picocolors.default.dim("# remove from specific agent")}
3985
+ ${import_picocolors.default.dim("$")} synk remove --all ${import_picocolors.default.dim("# remove all")}
3986
+ ${import_picocolors.default.dim("$")} synk remove --type agent ${import_picocolors.default.dim("# remove only agents")}
3987
+ `);
3988
+ }
3989
+ const __dirname = dirname(fileURLToPath(import.meta.url));
3990
+ function getVersion() {
3991
+ try {
3992
+ const pkgPath = join(__dirname, "..", "package.json");
3993
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
3994
+ } catch {
3995
+ return "0.0.0";
3996
+ }
3997
+ }
3998
+ const VERSION = getVersion();
3999
+ initTelemetry(VERSION);
4000
+ async function main() {
4001
+ const args = process.argv.slice(2);
4002
+ if (args.length === 0) {
4003
+ showBanner();
4004
+ return;
4005
+ }
4006
+ const command = args[0];
4007
+ const restArgs = args.slice(1);
4008
+ switch (command) {
4009
+ case "find":
4010
+ case "search":
4011
+ case "f":
4012
+ case "s":
4013
+ showLogo();
4014
+ logger.line();
4015
+ await runFind(restArgs);
4016
+ break;
4017
+ case "init":
4018
+ showLogo();
4019
+ logger.line();
4020
+ runInit(restArgs);
4021
+ break;
4022
+ case "i":
4023
+ case "install":
4024
+ case "a":
4025
+ case "add": {
4026
+ showLogo();
4027
+ const { source, options } = parseAddOptions(restArgs);
4028
+ await runAdd(source, options);
4029
+ break;
4030
+ }
4031
+ case "remove":
4032
+ case "rm":
4033
+ case "r":
4034
+ if (restArgs.includes("--help") || restArgs.includes("-h")) {
4035
+ showRemoveHelp();
4036
+ break;
4037
+ }
4038
+ const { skills, options: removeOptions } = parseRemoveOptions(restArgs);
4039
+ await removeCommand(skills, removeOptions);
4040
+ break;
4041
+ case "list":
4042
+ case "ls":
4043
+ await runList(restArgs);
4044
+ break;
4045
+ case "check":
4046
+ runCheck(restArgs);
4047
+ break;
4048
+ case "update":
4049
+ case "upgrade":
4050
+ runUpdate();
4051
+ break;
4052
+ case "--help":
4053
+ case "-h":
4054
+ showHelp();
4055
+ break;
4056
+ case "--version":
4057
+ case "-v":
4058
+ logger.log(VERSION);
4059
+ break;
4060
+ default:
4061
+ logger.error(`Unknown command: ${command}`);
4062
+ logger.log(`Run ${import_picocolors.default.bold("synk --help")} for usage.`);
4063
+ }
4064
+ }
4065
+ main();
4066
+ export {};