trusttools 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/index.js ADDED
@@ -0,0 +1,1635 @@
1
+ #!/usr/bin/env node
2
+
3
+ // node_modules/tsup/assets/esm_shims.js
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ var getFilename = () => fileURLToPath(import.meta.url);
7
+ var getDirname = () => path.dirname(getFilename());
8
+ var __dirname = /* @__PURE__ */ getDirname();
9
+
10
+ // src/index.ts
11
+ import { Command } from "commander";
12
+ import chalk9 from "chalk";
13
+ import { render as render3 } from "ink";
14
+ import React5 from "react";
15
+
16
+ // src/commands/add.ts
17
+ import fs5 from "fs";
18
+ import path6 from "path";
19
+ import simpleGit from "simple-git";
20
+ import chalk2 from "chalk";
21
+
22
+ // src/utils/logger.ts
23
+ import chalk from "chalk";
24
+ var Logger = class {
25
+ currentTask = null;
26
+ start(message) {
27
+ this.currentTask = message;
28
+ process.stdout.write(chalk.cyan(`\u23F3 ${message}
29
+ `));
30
+ }
31
+ succeed(message) {
32
+ const msg = message || this.currentTask || "Done";
33
+ process.stdout.write(chalk.green(`\u2714 ${msg}
34
+ `));
35
+ this.currentTask = null;
36
+ }
37
+ fail(message) {
38
+ const msg = message || this.currentTask || "Failed";
39
+ process.stdout.write(chalk.red(`\u2716 ${msg}
40
+ `));
41
+ this.currentTask = null;
42
+ }
43
+ info(message) {
44
+ console.log(chalk.blue(`\u2139 ${message}`));
45
+ }
46
+ warn(message) {
47
+ console.log(chalk.yellow(`\u26A0 ${message}`));
48
+ }
49
+ error(message) {
50
+ console.log(chalk.red(`\u2716 ${message}`));
51
+ }
52
+ };
53
+ function createLogger() {
54
+ return new Logger();
55
+ }
56
+
57
+ // src/utils/source.ts
58
+ function parseSource(source) {
59
+ source = source.trim().replace(/\/+$/, "");
60
+ if (source.startsWith("http://") || source.startsWith("https://")) {
61
+ const url = new URL(source);
62
+ if (url.hostname === "github.com") {
63
+ const parts2 = url.pathname.split("/").filter(Boolean);
64
+ if (parts2.length >= 2) {
65
+ return {
66
+ type: "github",
67
+ value: source,
68
+ owner: parts2[0],
69
+ repo: parts2[1].replace(".git", ""),
70
+ path: parts2.length > 2 ? parts2.slice(2).join("/") : void 0
71
+ };
72
+ }
73
+ }
74
+ return {
75
+ type: "url",
76
+ value: source
77
+ };
78
+ }
79
+ if (source.startsWith(".") || source.startsWith("/")) {
80
+ return {
81
+ type: "local",
82
+ value: source
83
+ };
84
+ }
85
+ const parts = source.split("/");
86
+ if (parts.length >= 2) {
87
+ return {
88
+ type: "github",
89
+ value: source,
90
+ owner: parts[0],
91
+ repo: parts[1],
92
+ path: parts.length > 2 ? parts.slice(2).join("/") : void 0
93
+ };
94
+ }
95
+ return {
96
+ type: "search",
97
+ value: source
98
+ };
99
+ }
100
+ function getGitHubCloneUrl(source) {
101
+ if (source.type !== "github" || !source.owner || !source.repo) {
102
+ return null;
103
+ }
104
+ return `https://github.com/${source.owner}/${source.repo}.git`;
105
+ }
106
+
107
+ // src/utils/parser.ts
108
+ import fs from "fs";
109
+ import path2 from "path";
110
+ import matter from "gray-matter";
111
+ function parseSkillFile(filePath, source) {
112
+ try {
113
+ const content = fs.readFileSync(filePath, "utf-8");
114
+ const { data, content: markdownContent } = matter(content);
115
+ if (!data.name || !data.description) {
116
+ console.warn(`Skipping ${filePath}: missing required fields (name, description)`);
117
+ return null;
118
+ }
119
+ return {
120
+ metadata: data,
121
+ content: markdownContent,
122
+ path: filePath,
123
+ source
124
+ };
125
+ } catch (error) {
126
+ console.error(`Error parsing ${filePath}:`, error);
127
+ return null;
128
+ }
129
+ }
130
+ function discoverSkills(dir, source) {
131
+ const skills = [];
132
+ const rootSkillPath = path2.join(dir, "SKILL.md");
133
+ if (fs.existsSync(rootSkillPath)) {
134
+ const skill = parseSkillFile(rootSkillPath, source);
135
+ if (skill) skills.push(skill);
136
+ }
137
+ const skillsDir = path2.join(dir, "skills");
138
+ if (fs.existsSync(skillsDir) && fs.statSync(skillsDir).isDirectory()) {
139
+ const entries = fs.readdirSync(skillsDir);
140
+ for (const entry of entries) {
141
+ const entryPath = path2.join(skillsDir, entry);
142
+ const stat = fs.statSync(entryPath);
143
+ if (stat.isDirectory()) {
144
+ const skillPath = path2.join(entryPath, "SKILL.md");
145
+ if (fs.existsSync(skillPath)) {
146
+ const skill = parseSkillFile(skillPath, source);
147
+ if (skill) skills.push(skill);
148
+ }
149
+ } else if (entry === "SKILL.md") {
150
+ const skill = parseSkillFile(entryPath, source);
151
+ if (skill) skills.push(skill);
152
+ }
153
+ }
154
+ }
155
+ const recursiveSearch = (currentDir, depth = 0, maxDepth = 2) => {
156
+ if (depth > maxDepth) return;
157
+ try {
158
+ const entries = fs.readdirSync(currentDir);
159
+ for (const entry of entries) {
160
+ if (entry.startsWith(".") || entry === "node_modules") continue;
161
+ const entryPath = path2.join(currentDir, entry);
162
+ const stat = fs.statSync(entryPath);
163
+ if (stat.isDirectory() && entryPath !== skillsDir) {
164
+ const skillPath = path2.join(entryPath, "SKILL.md");
165
+ if (fs.existsSync(skillPath)) {
166
+ const skill = parseSkillFile(skillPath, source);
167
+ if (skill) skills.push(skill);
168
+ }
169
+ recursiveSearch(entryPath, depth + 1, maxDepth);
170
+ }
171
+ }
172
+ } catch (error) {
173
+ }
174
+ };
175
+ recursiveSearch(dir);
176
+ return skills;
177
+ }
178
+
179
+ // src/utils/agents.ts
180
+ import * as path3 from "path";
181
+ import * as fs2 from "fs";
182
+ import * as os from "os";
183
+ function getAllAgents(cwd = process.cwd()) {
184
+ const homeDir = os.homedir();
185
+ const agents = [
186
+ {
187
+ type: "adal",
188
+ name: "AdaL",
189
+ detected: false,
190
+ skillsPath: path3.join(cwd, ".adal", "skills"),
191
+ globalPath: path3.join(homeDir, ".adal", "skills")
192
+ },
193
+ {
194
+ type: "amp",
195
+ name: "Amp",
196
+ detected: false,
197
+ skillsPath: path3.join(cwd, ".agents", "skills"),
198
+ globalPath: path3.join(homeDir, ".agents", "skills"),
199
+ isUniversal: true
200
+ },
201
+ {
202
+ type: "antigravity",
203
+ name: "Antigravity",
204
+ detected: false,
205
+ skillsPath: path3.join(cwd, ".agent", "skills"),
206
+ globalPath: path3.join(homeDir, ".gemini", "antigravity", "skills")
207
+ },
208
+ {
209
+ type: "augment",
210
+ name: "Augment",
211
+ detected: false,
212
+ skillsPath: path3.join(cwd, ".augment", "rules"),
213
+ globalPath: path3.join(homeDir, ".augment", "rules")
214
+ },
215
+ {
216
+ type: "claude-code",
217
+ name: "Claude Code",
218
+ detected: false,
219
+ skillsPath: path3.join(cwd, ".claude", "skills"),
220
+ globalPath: path3.join(homeDir, ".claude", "skills")
221
+ },
222
+ {
223
+ type: "cline",
224
+ name: "Cline",
225
+ detected: false,
226
+ skillsPath: path3.join(cwd, ".cline", "skills"),
227
+ globalPath: path3.join(homeDir, ".cline", "skills")
228
+ },
229
+ {
230
+ type: "codebuddy",
231
+ name: "CodeBuddy",
232
+ detected: false,
233
+ skillsPath: path3.join(cwd, ".codebuddy", "skills"),
234
+ globalPath: path3.join(homeDir, ".codebuddy", "skills")
235
+ },
236
+ {
237
+ type: "codex",
238
+ name: "Codex",
239
+ detected: false,
240
+ skillsPath: path3.join(cwd, ".agents", "skills"),
241
+ globalPath: path3.join(homeDir, ".agents", "skills"),
242
+ isUniversal: true
243
+ },
244
+ {
245
+ type: "command-code",
246
+ name: "Command Code",
247
+ detected: false,
248
+ skillsPath: path3.join(cwd, ".commandcode", "skills"),
249
+ globalPath: path3.join(homeDir, ".commandcode", "skills")
250
+ },
251
+ {
252
+ type: "continue",
253
+ name: "Continue",
254
+ detected: false,
255
+ skillsPath: path3.join(cwd, ".continue", "skills"),
256
+ globalPath: path3.join(homeDir, ".continue", "skills")
257
+ },
258
+ {
259
+ type: "cortex",
260
+ name: "Cortex Code",
261
+ detected: false,
262
+ skillsPath: path3.join(cwd, ".cortex", "skills"),
263
+ globalPath: path3.join(homeDir, ".snowflake", "cortex", "skills")
264
+ },
265
+ {
266
+ type: "crush",
267
+ name: "Crush",
268
+ detected: false,
269
+ skillsPath: path3.join(cwd, ".crush", "skills"),
270
+ globalPath: path3.join(homeDir, ".config", "crush", "skills")
271
+ },
272
+ {
273
+ type: "cursor",
274
+ name: "Cursor",
275
+ detected: false,
276
+ skillsPath: path3.join(cwd, ".agents", "skills"),
277
+ globalPath: path3.join(homeDir, ".cursor", "skills"),
278
+ isUniversal: true
279
+ },
280
+ {
281
+ type: "droid",
282
+ name: "Droid",
283
+ detected: false,
284
+ skillsPath: path3.join(cwd, ".agents", "skills"),
285
+ globalPath: path3.join(homeDir, ".agents", "skills"),
286
+ isUniversal: true
287
+ },
288
+ {
289
+ type: "gemini-cli",
290
+ name: "Gemini CLI",
291
+ detected: false,
292
+ skillsPath: path3.join(cwd, ".agents", "skills"),
293
+ globalPath: path3.join(homeDir, ".agents", "skills"),
294
+ isUniversal: true
295
+ },
296
+ {
297
+ type: "github-copilot",
298
+ name: "GitHub Copilot",
299
+ detected: false,
300
+ skillsPath: path3.join(cwd, ".agents", "skills"),
301
+ globalPath: path3.join(homeDir, ".agents", "skills"),
302
+ isUniversal: true
303
+ },
304
+ {
305
+ type: "goose",
306
+ name: "Goose",
307
+ detected: false,
308
+ skillsPath: path3.join(cwd, ".goose", "skills"),
309
+ globalPath: path3.join(homeDir, ".config", "goose", "skills")
310
+ },
311
+ {
312
+ type: "iflow-cli",
313
+ name: "iFlow CLI",
314
+ detected: false,
315
+ skillsPath: path3.join(cwd, ".iflow", "skills"),
316
+ globalPath: path3.join(homeDir, ".iflow", "skills")
317
+ },
318
+ {
319
+ type: "junie",
320
+ name: "Junie",
321
+ detected: false,
322
+ skillsPath: path3.join(cwd, ".junie", "skills"),
323
+ globalPath: path3.join(homeDir, ".junie", "skills")
324
+ },
325
+ {
326
+ type: "kilo",
327
+ name: "Kilo Code",
328
+ detected: false,
329
+ skillsPath: path3.join(cwd, ".kilocode", "skills"),
330
+ globalPath: path3.join(homeDir, ".kilocode", "skills")
331
+ },
332
+ {
333
+ type: "kimi-cli",
334
+ name: "Kimi Code CLI",
335
+ detected: false,
336
+ skillsPath: path3.join(cwd, ".agents", "skills"),
337
+ globalPath: path3.join(homeDir, ".agents", "skills"),
338
+ isUniversal: true
339
+ },
340
+ {
341
+ type: "kiro-cli",
342
+ name: "Kiro CLI",
343
+ detected: false,
344
+ skillsPath: path3.join(cwd, ".kiro", "skills"),
345
+ globalPath: path3.join(homeDir, ".kiro", "skills")
346
+ },
347
+ {
348
+ type: "kode",
349
+ name: "Kode",
350
+ detected: false,
351
+ skillsPath: path3.join(cwd, ".kode", "skills"),
352
+ globalPath: path3.join(homeDir, ".kode", "skills")
353
+ },
354
+ {
355
+ type: "mcpjam",
356
+ name: "MCPJam",
357
+ detected: false,
358
+ skillsPath: path3.join(cwd, ".mcpjam", "skills"),
359
+ globalPath: path3.join(homeDir, ".mcpjam", "skills")
360
+ },
361
+ {
362
+ type: "mistral-vibe",
363
+ name: "Mistral Vibe",
364
+ detected: false,
365
+ skillsPath: path3.join(cwd, ".vibe", "skills"),
366
+ globalPath: path3.join(homeDir, ".vibe", "skills")
367
+ },
368
+ {
369
+ type: "mux",
370
+ name: "Mux",
371
+ detected: false,
372
+ skillsPath: path3.join(cwd, ".mux", "skills"),
373
+ globalPath: path3.join(homeDir, ".mux", "skills")
374
+ },
375
+ {
376
+ type: "neovate",
377
+ name: "Neovate",
378
+ detected: false,
379
+ skillsPath: path3.join(cwd, ".neovate", "skills"),
380
+ globalPath: path3.join(homeDir, ".neovate", "skills")
381
+ },
382
+ {
383
+ type: "openclaw",
384
+ name: "OpenClaw",
385
+ detected: false,
386
+ skillsPath: path3.join(cwd, "skills"),
387
+ globalPath: path3.join(homeDir, ".openclaw", "skills")
388
+ },
389
+ {
390
+ type: "opencode",
391
+ name: "OpenCode",
392
+ detected: false,
393
+ skillsPath: path3.join(cwd, ".agents", "skills"),
394
+ globalPath: path3.join(homeDir, ".agents", "skills"),
395
+ isUniversal: true
396
+ },
397
+ {
398
+ type: "openhands",
399
+ name: "OpenHands",
400
+ detected: false,
401
+ skillsPath: path3.join(cwd, ".openhands", "skills"),
402
+ globalPath: path3.join(homeDir, ".openhands", "skills")
403
+ },
404
+ {
405
+ type: "pi",
406
+ name: "Pi",
407
+ detected: false,
408
+ skillsPath: path3.join(cwd, ".pi", "skills"),
409
+ globalPath: path3.join(homeDir, ".pi", "agent", "skills")
410
+ },
411
+ {
412
+ type: "pochi",
413
+ name: "Pochi",
414
+ detected: false,
415
+ skillsPath: path3.join(cwd, ".pochi", "skills"),
416
+ globalPath: path3.join(homeDir, ".pochi", "skills")
417
+ },
418
+ {
419
+ type: "qoder",
420
+ name: "Qoder",
421
+ detected: false,
422
+ skillsPath: path3.join(cwd, ".qoder", "skills"),
423
+ globalPath: path3.join(homeDir, ".qoder", "skills")
424
+ },
425
+ {
426
+ type: "qwen-code",
427
+ name: "Qwen Code",
428
+ detected: false,
429
+ skillsPath: path3.join(cwd, ".qwen", "skills"),
430
+ globalPath: path3.join(homeDir, ".qwen", "skills")
431
+ },
432
+ {
433
+ type: "replit",
434
+ name: "Replit",
435
+ detected: false,
436
+ skillsPath: path3.join(cwd, ".agents", "skills"),
437
+ isUniversal: true
438
+ },
439
+ {
440
+ type: "roo",
441
+ name: "Roo Code",
442
+ detected: false,
443
+ skillsPath: path3.join(cwd, ".roo", "skills"),
444
+ globalPath: path3.join(homeDir, ".roo", "skills")
445
+ },
446
+ {
447
+ type: "trae",
448
+ name: "Trae",
449
+ detected: false,
450
+ skillsPath: path3.join(cwd, ".trae", "skills"),
451
+ globalPath: path3.join(homeDir, ".trae", "skills")
452
+ },
453
+ {
454
+ type: "trae-cn",
455
+ name: "Trae CN",
456
+ detected: false,
457
+ skillsPath: path3.join(cwd, ".trae", "skills"),
458
+ globalPath: path3.join(homeDir, ".trae-cn", "skills")
459
+ },
460
+ {
461
+ type: "windsurf",
462
+ name: "Windsurf",
463
+ detected: false,
464
+ skillsPath: path3.join(cwd, ".windsurf", "skills"),
465
+ globalPath: path3.join(homeDir, ".codeium", "windsurf", "skills")
466
+ },
467
+ {
468
+ type: "zencoder",
469
+ name: "Zencoder",
470
+ detected: false,
471
+ skillsPath: path3.join(cwd, ".zencoder", "skills"),
472
+ globalPath: path3.join(homeDir, ".zencoder", "skills")
473
+ },
474
+ {
475
+ type: "universal",
476
+ name: "Universal",
477
+ detected: false,
478
+ skillsPath: path3.join(cwd, ".agents", "skills"),
479
+ globalPath: path3.join(homeDir, ".agents", "skills"),
480
+ isUniversal: true
481
+ }
482
+ ];
483
+ return agents;
484
+ }
485
+ function detectAgents(cwd = process.cwd()) {
486
+ const agents = getAllAgents(cwd);
487
+ for (const agent of agents) {
488
+ const agentDir = path3.dirname(agent.skillsPath);
489
+ agent.detected = fs2.existsSync(agentDir);
490
+ }
491
+ return agents;
492
+ }
493
+ function getUniversalSkillsPath(cwd = process.cwd()) {
494
+ return path3.join(cwd, ".agents", "skills");
495
+ }
496
+ function ensureUniversalSkillsDir(cwd = process.cwd()) {
497
+ const skillsPath = getUniversalSkillsPath(cwd);
498
+ if (!fs2.existsSync(skillsPath)) {
499
+ fs2.mkdirSync(skillsPath, { recursive: true });
500
+ }
501
+ return skillsPath;
502
+ }
503
+
504
+ // src/utils/symlink.ts
505
+ import fs3 from "fs";
506
+ import path4 from "path";
507
+ function createSymlink(target, linkPath, type = "dir") {
508
+ try {
509
+ if (fs3.existsSync(linkPath)) {
510
+ const stats = fs3.lstatSync(linkPath);
511
+ if (stats.isSymbolicLink()) {
512
+ const existingTarget = fs3.readlinkSync(linkPath);
513
+ if (existingTarget === target) {
514
+ return true;
515
+ }
516
+ fs3.unlinkSync(linkPath);
517
+ } else {
518
+ console.warn(`Path exists but is not a symlink: ${linkPath}`);
519
+ return false;
520
+ }
521
+ }
522
+ const parentDir = path4.dirname(linkPath);
523
+ if (!fs3.existsSync(parentDir)) {
524
+ fs3.mkdirSync(parentDir, { recursive: true });
525
+ }
526
+ fs3.symlinkSync(target, linkPath, type);
527
+ return true;
528
+ } catch (error) {
529
+ if (error.code === "EPERM" && process.platform === "win32") {
530
+ console.error(
531
+ "Permission denied. On Windows, you need to either:\n1. Run as Administrator, or\n2. Enable Developer Mode in Windows Settings"
532
+ );
533
+ } else {
534
+ console.error(`Failed to create symlink: ${error.message}`);
535
+ }
536
+ return false;
537
+ }
538
+ }
539
+ function linkSkillToAgents(skillName, universalPath, agents) {
540
+ const linked = [];
541
+ const skillSource = path4.join(universalPath, skillName);
542
+ for (const agent of agents) {
543
+ const linkPath = path4.join(agent.skillsPath, skillName);
544
+ if (!fs3.existsSync(agent.skillsPath)) {
545
+ fs3.mkdirSync(agent.skillsPath, { recursive: true });
546
+ }
547
+ if (createSymlink(skillSource, linkPath, "dir")) {
548
+ linked.push(agent.type);
549
+ }
550
+ }
551
+ return {
552
+ success: linked.length > 0,
553
+ linked
554
+ };
555
+ }
556
+
557
+ // src/utils/analytics.ts
558
+ import * as fs4 from "fs";
559
+ import * as path5 from "path";
560
+ import * as os2 from "os";
561
+ import * as crypto from "crypto";
562
+ var ANALYTICS_DIR = path5.join(os2.homedir(), ".trusttools");
563
+ var ANALYTICS_FILE = path5.join(ANALYTICS_DIR, "analytics.json");
564
+ var MACHINE_ID_FILE = path5.join(ANALYTICS_DIR, "machine-id");
565
+ var ANALYTICS_ENABLED_FILE = path5.join(ANALYTICS_DIR, "analytics-enabled");
566
+ function getMachineId() {
567
+ try {
568
+ if (fs4.existsSync(MACHINE_ID_FILE)) {
569
+ return fs4.readFileSync(MACHINE_ID_FILE, "utf-8").trim();
570
+ }
571
+ const machineId = crypto.randomUUID();
572
+ ensureAnalyticsDir();
573
+ fs4.writeFileSync(MACHINE_ID_FILE, machineId, "utf-8");
574
+ return machineId;
575
+ } catch (error) {
576
+ return "unknown";
577
+ }
578
+ }
579
+ function isAnalyticsEnabled() {
580
+ try {
581
+ if (process.env.SKILLSMAP_ANALYTICS === "false") {
582
+ return false;
583
+ }
584
+ if (fs4.existsSync(ANALYTICS_ENABLED_FILE)) {
585
+ const enabled = fs4.readFileSync(ANALYTICS_ENABLED_FILE, "utf-8").trim();
586
+ return enabled === "true";
587
+ }
588
+ return true;
589
+ } catch (error) {
590
+ return true;
591
+ }
592
+ }
593
+ function ensureAnalyticsDir() {
594
+ if (!fs4.existsSync(ANALYTICS_DIR)) {
595
+ fs4.mkdirSync(ANALYTICS_DIR, { recursive: true });
596
+ }
597
+ }
598
+ function loadInstallationRecords() {
599
+ try {
600
+ if (!fs4.existsSync(ANALYTICS_FILE)) {
601
+ return [];
602
+ }
603
+ const data = fs4.readFileSync(ANALYTICS_FILE, "utf-8");
604
+ return JSON.parse(data);
605
+ } catch (error) {
606
+ return [];
607
+ }
608
+ }
609
+ function saveInstallationRecords(records) {
610
+ ensureAnalyticsDir();
611
+ fs4.writeFileSync(ANALYTICS_FILE, JSON.stringify(records, null, 2), "utf-8");
612
+ }
613
+ function trackInstallation(skillName, skillSlug, source, sourceType, agents, isGlobal, version) {
614
+ const record = {
615
+ id: crypto.randomUUID(),
616
+ skillName,
617
+ skillSlug,
618
+ source,
619
+ sourceType,
620
+ agents,
621
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
622
+ isGlobal,
623
+ machineId: getMachineId(),
624
+ version
625
+ };
626
+ const records = loadInstallationRecords();
627
+ records.push(record);
628
+ saveInstallationRecords(records);
629
+ if (isAnalyticsEnabled()) {
630
+ sendAnalytics("install", record).catch(() => {
631
+ });
632
+ }
633
+ return record;
634
+ }
635
+ function trackRemoval(skillName, skillSlug, agents) {
636
+ const record = {
637
+ id: crypto.randomUUID(),
638
+ skillName,
639
+ skillSlug,
640
+ source: "",
641
+ sourceType: "local",
642
+ agents,
643
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
644
+ isGlobal: false,
645
+ machineId: getMachineId()
646
+ };
647
+ if (isAnalyticsEnabled()) {
648
+ sendAnalytics("remove", record).catch(() => {
649
+ });
650
+ }
651
+ }
652
+ async function sendAnalytics(event, record) {
653
+ try {
654
+ const payload = {
655
+ event,
656
+ skillName: record.skillName,
657
+ skillSlug: record.skillSlug,
658
+ source: record.source,
659
+ sourceType: record.sourceType,
660
+ agents: record.agents,
661
+ timestamp: record.timestamp,
662
+ isGlobal: record.isGlobal,
663
+ machineId: record.machineId,
664
+ version: record.version,
665
+ cliVersion: getCliVersion()
666
+ };
667
+ const analyticsUrl = process.env.SKILLSMAP_ANALYTICS_URL || "http://localhost:8000/api/analytics/track";
668
+ const response = await fetch(analyticsUrl, {
669
+ method: "POST",
670
+ headers: {
671
+ "Content-Type": "application/json"
672
+ },
673
+ body: JSON.stringify(payload)
674
+ });
675
+ if (!response.ok) {
676
+ throw new Error(`Analytics request failed: ${response.status}`);
677
+ }
678
+ } catch (error) {
679
+ console.debug("Analytics error:", error);
680
+ }
681
+ }
682
+ function getCliVersion() {
683
+ try {
684
+ const packageJsonPath = path5.join(__dirname, "../../package.json");
685
+ const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
686
+ return packageJson.version || "1.0.0";
687
+ } catch (error) {
688
+ return "1.0.0";
689
+ }
690
+ }
691
+ function getInstallationStats() {
692
+ const records = loadInstallationRecords();
693
+ const skillsByAgent = {};
694
+ const skillCounts = {};
695
+ records.forEach((record) => {
696
+ record.agents.forEach((agent) => {
697
+ skillsByAgent[agent] = (skillsByAgent[agent] || 0) + 1;
698
+ });
699
+ skillCounts[record.skillSlug] = (skillCounts[record.skillSlug] || 0) + 1;
700
+ });
701
+ const topSkills = Object.entries(skillCounts).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count).slice(0, 10);
702
+ const recentInstalls = records.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, 20);
703
+ return {
704
+ totalInstalls: records.length,
705
+ skillsByAgent,
706
+ topSkills,
707
+ recentInstalls
708
+ };
709
+ }
710
+
711
+ // src/commands/add.ts
712
+ async function addSkill(source, options = {}) {
713
+ const cwd = process.cwd();
714
+ const logger = createLogger();
715
+ logger.start("Parsing source...");
716
+ try {
717
+ const parsedSource = parseSource(source);
718
+ logger.succeed("Source parsed: " + parsedSource.type);
719
+ let skillsDir;
720
+ let tempDir = null;
721
+ if (parsedSource.type === "github") {
722
+ logger.start("Cloning repository...");
723
+ const cloneUrl = getGitHubCloneUrl(parsedSource);
724
+ if (!cloneUrl) {
725
+ throw new Error("Invalid GitHub URL");
726
+ }
727
+ tempDir = path6.join(cwd, ".tmp", `${parsedSource.owner}-${parsedSource.repo}`);
728
+ if (fs5.existsSync(tempDir)) {
729
+ fs5.rmSync(tempDir, { recursive: true, force: true });
730
+ }
731
+ fs5.mkdirSync(tempDir, { recursive: true });
732
+ const git = simpleGit();
733
+ await git.clone(cloneUrl, tempDir, ["--depth", "1"]);
734
+ skillsDir = parsedSource.path ? path6.join(tempDir, parsedSource.path) : tempDir;
735
+ logger.succeed("Repository cloned");
736
+ } else if (parsedSource.type === "local") {
737
+ skillsDir = path6.resolve(cwd, parsedSource.value);
738
+ if (!fs5.existsSync(skillsDir)) {
739
+ throw new Error(`Local path not found: ${skillsDir}`);
740
+ }
741
+ logger.succeed("Local path found");
742
+ } else {
743
+ throw new Error(`Unsupported source type: ${parsedSource.type}`);
744
+ }
745
+ logger.start("Discovering skills...");
746
+ let skills = discoverSkills(skillsDir, source);
747
+ if (skills.length === 0) {
748
+ logger.fail("No valid skills found");
749
+ console.log(chalk2.yellow("\nA valid skill must have a SKILL.md file with:"));
750
+ console.log(chalk2.yellow(" - name: <skill name>"));
751
+ console.log(chalk2.yellow(" - description: <description>"));
752
+ return;
753
+ }
754
+ if (options.skill) {
755
+ const skillFilter = options.skill.toLowerCase();
756
+ skills = skills.filter(
757
+ (s) => s.metadata.name.toLowerCase().replace(/\s+/g, "-") === skillFilter || s.metadata.name.toLowerCase() === skillFilter
758
+ );
759
+ if (skills.length === 0) {
760
+ logger.fail(`No skill found matching: ${options.skill}`);
761
+ console.log(chalk2.yellow("\nAvailable skills:"));
762
+ const allSkills = discoverSkills(skillsDir, source);
763
+ allSkills.forEach((s) => {
764
+ console.log(chalk2.gray(` - ${s.metadata.name.toLowerCase().replace(/\s+/g, "-")}`));
765
+ });
766
+ return;
767
+ }
768
+ logger.succeed(`Found skill: ${skills[0].metadata.name}`);
769
+ } else {
770
+ logger.succeed(`Found ${skills.length} skill(s)`);
771
+ }
772
+ const universalPath = ensureUniversalSkillsDir(cwd);
773
+ const agents = detectAgents(cwd);
774
+ const detectedAgents = agents.filter((a) => a.detected);
775
+ if (detectedAgents.length === 0) {
776
+ console.log(chalk2.yellow("\nNo AI agents detected in current directory."));
777
+ console.log(chalk2.yellow("Looking for: .cursor, .claude, .windsurf"));
778
+ } else {
779
+ console.log(chalk2.green(`
780
+ Detected agents: ${detectedAgents.map((a) => a.type).join(", ")}`));
781
+ }
782
+ for (const skill of skills) {
783
+ logger.start(`Installing ${skill.metadata.name}...`);
784
+ const skillName = skill.metadata.name.toLowerCase().replace(/\s+/g, "-");
785
+ const skillDestDir = path6.join(universalPath, skillName);
786
+ const skillSourceDir = path6.dirname(skill.path);
787
+ if (fs5.existsSync(skillDestDir)) {
788
+ fs5.rmSync(skillDestDir, { recursive: true, force: true });
789
+ }
790
+ fs5.mkdirSync(skillDestDir, { recursive: true });
791
+ copyDirectory(skillSourceDir, skillDestDir);
792
+ const sourceFile = path6.join(skillDestDir, ".source");
793
+ fs5.writeFileSync(sourceFile, source, "utf-8");
794
+ console.log(chalk2.cyan(` Linking ${skill.metadata.name} to agents...`));
795
+ const { success, linked } = linkSkillToAgents(skillName, universalPath, detectedAgents);
796
+ if (success) {
797
+ logger.succeed(
798
+ `${skill.metadata.name} installed and linked to: ${linked.join(", ")}`
799
+ );
800
+ } else {
801
+ logger.warn(
802
+ `${skill.metadata.name} installed but not linked (no agents detected)`
803
+ );
804
+ }
805
+ console.log(chalk2.gray(` Location: ${skillDestDir}`));
806
+ trackInstallation(
807
+ skill.metadata.name,
808
+ skillName,
809
+ source,
810
+ parsedSource.type,
811
+ linked.map((a) => a),
812
+ options.global || false,
813
+ skill.metadata.version
814
+ );
815
+ }
816
+ if (tempDir && fs5.existsSync(tempDir)) {
817
+ fs5.rmSync(tempDir, { recursive: true, force: true });
818
+ }
819
+ console.log(chalk2.green("\n\u2713 All skills installed successfully!"));
820
+ } catch (error) {
821
+ logger.fail("Installation failed");
822
+ console.error(chalk2.red(`
823
+ Error: ${error.message}`));
824
+ throw error;
825
+ }
826
+ }
827
+ function copyDirectory(src, dest) {
828
+ if (!fs5.existsSync(dest)) {
829
+ fs5.mkdirSync(dest, { recursive: true });
830
+ }
831
+ const entries = fs5.readdirSync(src, { withFileTypes: true });
832
+ for (const entry of entries) {
833
+ const srcPath = path6.join(src, entry.name);
834
+ const destPath = path6.join(dest, entry.name);
835
+ if (entry.isDirectory()) {
836
+ copyDirectory(srcPath, destPath);
837
+ } else {
838
+ fs5.copyFileSync(srcPath, destPath);
839
+ }
840
+ }
841
+ }
842
+
843
+ // src/commands/list.ts
844
+ import fs6 from "fs";
845
+ import path7 from "path";
846
+ import chalk3 from "chalk";
847
+ function listSkills() {
848
+ const cwd = process.cwd();
849
+ const universalPath = getUniversalSkillsPath(cwd);
850
+ if (!fs6.existsSync(universalPath)) {
851
+ console.log(chalk3.yellow("No skills installed yet."));
852
+ console.log(chalk3.gray(`Run 'trusttools add <source>' to install skills.`));
853
+ return;
854
+ }
855
+ const entries = fs6.readdirSync(universalPath);
856
+ const skills = entries.filter((entry) => {
857
+ const entryPath = path7.join(universalPath, entry);
858
+ return fs6.statSync(entryPath).isDirectory();
859
+ });
860
+ if (skills.length === 0) {
861
+ console.log(chalk3.yellow("No skills installed yet."));
862
+ return;
863
+ }
864
+ console.log(chalk3.bold(`
865
+ \u{1F4E6} Installed Skills (${skills.length}):
866
+ `));
867
+ for (const skillDir of skills) {
868
+ const skillPath = path7.join(universalPath, skillDir);
869
+ const skillMdPath = path7.join(skillPath, "SKILL.md");
870
+ if (fs6.existsSync(skillMdPath)) {
871
+ const skill = parseSkillFile(skillMdPath, "local");
872
+ if (skill) {
873
+ console.log(chalk3.green(` \u2713 ${skill.metadata.name}`));
874
+ console.log(chalk3.gray(` ${skill.metadata.description}`));
875
+ if (skill.metadata.version) {
876
+ console.log(chalk3.gray(` Version: ${skill.metadata.version}`));
877
+ }
878
+ if (skill.metadata.tags && skill.metadata.tags.length > 0) {
879
+ console.log(chalk3.gray(` Tags: ${skill.metadata.tags.join(", ")}`));
880
+ }
881
+ console.log(chalk3.gray(` Path: ${skillPath}`));
882
+ console.log();
883
+ }
884
+ } else {
885
+ console.log(chalk3.yellow(` \u26A0 ${skillDir} (no SKILL.md found)`));
886
+ console.log();
887
+ }
888
+ }
889
+ }
890
+
891
+ // src/commands/find.ts
892
+ import fs7 from "fs";
893
+ import path8 from "path";
894
+ import simpleGit2 from "simple-git";
895
+ import chalk4 from "chalk";
896
+ import { render } from "ink";
897
+ import React2 from "react";
898
+
899
+ // src/ui/SkillSelector.tsx
900
+ import React, { useState } from "react";
901
+ import { Box, Text, useInput } from "ink";
902
+ var SkillSelector = ({ skills, onSelect, onCancel }) => {
903
+ const [selectedIndex, setSelectedIndex] = useState(0);
904
+ useInput((input, key) => {
905
+ if (key.upArrow) {
906
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
907
+ } else if (key.downArrow) {
908
+ setSelectedIndex((prev) => Math.min(skills.length - 1, prev + 1));
909
+ } else if (key.return) {
910
+ onSelect(skills[selectedIndex]);
911
+ } else if (key.escape || input === "q") {
912
+ onCancel();
913
+ }
914
+ });
915
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, "\u{1F4E6} Found ", skills.length, " skill(s). Use \u2191\u2193 to navigate, Enter to select, Esc to cancel:")), skills.map((skill, index) => {
916
+ const getSecurityColor = (score) => {
917
+ if (!score) return "gray";
918
+ if (score >= 81) return "green";
919
+ if (score >= 61) return "yellow";
920
+ if (score >= 41) return "yellow";
921
+ if (score >= 21) return "red";
922
+ return "red";
923
+ };
924
+ const getSecurityLabel = (score) => {
925
+ if (!score) return "?";
926
+ if (score >= 81) return "\u2713";
927
+ if (score >= 61) return "\u26A0";
928
+ if (score >= 41) return "\u26A0";
929
+ if (score >= 21) return "\u2717";
930
+ return "\u2717";
931
+ };
932
+ return /* @__PURE__ */ React.createElement(Box, { key: index, marginLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: index === selectedIndex ? "green" : "white" }, index === selectedIndex ? "\u25B6 " : " ", /* @__PURE__ */ React.createElement(Text, { bold: true }, skill.metadata.name), skill.metadata.security_score !== void 0 && /* @__PURE__ */ React.createElement(Text, { color: getSecurityColor(skill.metadata.security_score) }, " [", getSecurityLabel(skill.metadata.security_score), " ", skill.metadata.security_score, "]"), skill.metadata.install_count !== void 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " (\u2193", skill.metadata.install_count, ")"))), /* @__PURE__ */ React.createElement(Box, { marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, skill.metadata.description)));
933
+ }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, skills[selectedIndex]?.metadata.tags && `Tags: ${skills[selectedIndex].metadata.tags.join(", ")}`)));
934
+ };
935
+
936
+ // src/commands/find.ts
937
+ import dotenv from "dotenv";
938
+ dotenv.config();
939
+ async function findFromAPI(query) {
940
+ const logger = createLogger();
941
+ logger.start(`Searching for "${query}"...`);
942
+ try {
943
+ const apiUrl = process.env.TRUSTTOOLS_API_URL || "https://skills.seebug.ai";
944
+ const response = await fetch(`${apiUrl}/api/skills?search=${encodeURIComponent(query)}&limit=20`);
945
+ if (!response.ok) {
946
+ throw new Error(`API request failed: ${response.statusText}`);
947
+ }
948
+ const data = await response.json();
949
+ if (!data.success || !data.data || data.data.length === 0) {
950
+ logger.fail("No skills found");
951
+ console.log(chalk4.yellow(`
952
+ No skills found for "${query}"`));
953
+ console.log(chalk4.gray("\nTry different keywords or browse skills at:"));
954
+ console.log(chalk4.cyan(` ${apiUrl}`));
955
+ return;
956
+ }
957
+ logger.succeed(`Found ${data.data.length} skill(s)`);
958
+ const skills = data.data.map((skill) => {
959
+ let skillPath = skill.repo_path || "";
960
+ if (skillPath.endsWith("/SKILL.md")) {
961
+ skillPath = skillPath.slice(0, -9);
962
+ } else if (skillPath.endsWith("SKILL.md")) {
963
+ skillPath = skillPath.slice(0, -8);
964
+ }
965
+ return {
966
+ metadata: {
967
+ name: skill.name,
968
+ description: skill.description,
969
+ version: skill.version,
970
+ author: skill.author,
971
+ tags: skill.tags || [],
972
+ security_score: skill.security_score,
973
+ security_level: skill.security_level,
974
+ install_count: skill.install_count
975
+ },
976
+ content: "",
977
+ path: skillPath,
978
+ source: `${skill.repo_owner}/${skill.repo_name}${skillPath ? "/" + skillPath : ""}`
979
+ };
980
+ });
981
+ const { waitUntilExit } = render(
982
+ React2.createElement(SkillSelector, {
983
+ skills,
984
+ onSelect: (skill) => {
985
+ console.log(chalk4.green(`
986
+ \u2713 Selected: ${skill.metadata.name}`));
987
+ console.log(chalk4.gray(`
988
+ To install this skill, run:`));
989
+ console.log(chalk4.cyan(` trusttools add ${skill.source}`));
990
+ process.exit(0);
991
+ },
992
+ onCancel: () => {
993
+ console.log(chalk4.yellow("\nCancelled"));
994
+ process.exit(0);
995
+ }
996
+ })
997
+ );
998
+ await waitUntilExit();
999
+ } catch (error) {
1000
+ logger.fail("Search failed");
1001
+ console.error(chalk4.red(`
1002
+ Error: ${error.message}`));
1003
+ throw error;
1004
+ }
1005
+ }
1006
+ async function findFromRepository(source, parsedSource) {
1007
+ const cwd = process.cwd();
1008
+ const logger = createLogger();
1009
+ let skillsDir;
1010
+ let tempDir = null;
1011
+ try {
1012
+ if (parsedSource.type === "github") {
1013
+ logger.start("Cloning repository...");
1014
+ const cloneUrl = getGitHubCloneUrl(parsedSource);
1015
+ if (!cloneUrl) {
1016
+ throw new Error("Invalid GitHub URL");
1017
+ }
1018
+ tempDir = path8.join(cwd, ".tmp", `${parsedSource.owner}-${parsedSource.repo}`);
1019
+ if (fs7.existsSync(tempDir)) {
1020
+ fs7.rmSync(tempDir, { recursive: true, force: true });
1021
+ }
1022
+ fs7.mkdirSync(tempDir, { recursive: true });
1023
+ const git = simpleGit2();
1024
+ await git.clone(cloneUrl, tempDir, ["--depth", "1"]);
1025
+ skillsDir = parsedSource.path ? path8.join(tempDir, parsedSource.path) : tempDir;
1026
+ logger.succeed("Repository cloned");
1027
+ } else if (parsedSource.type === "local") {
1028
+ skillsDir = path8.resolve(cwd, parsedSource.value);
1029
+ if (!fs7.existsSync(skillsDir)) {
1030
+ throw new Error(`Local path not found: ${skillsDir}`);
1031
+ }
1032
+ logger.succeed("Local path found");
1033
+ } else {
1034
+ throw new Error(`Unsupported source type: ${parsedSource.type}`);
1035
+ }
1036
+ logger.start("Discovering skills...");
1037
+ const skills = discoverSkills(skillsDir, source);
1038
+ if (skills.length === 0) {
1039
+ logger.fail("No valid skills found");
1040
+ console.log(chalk4.yellow("\nA valid skill must have a SKILL.md file with:"));
1041
+ console.log(chalk4.yellow(" - name: <skill name>"));
1042
+ console.log(chalk4.yellow(" - description: <description>"));
1043
+ if (tempDir && fs7.existsSync(tempDir)) {
1044
+ fs7.rmSync(tempDir, { recursive: true, force: true });
1045
+ }
1046
+ return;
1047
+ }
1048
+ logger.succeed(`Found ${skills.length} skill(s)`);
1049
+ const { waitUntilExit } = render(
1050
+ React2.createElement(SkillSelector, {
1051
+ skills,
1052
+ onSelect: (skill) => {
1053
+ console.log(chalk4.green(`
1054
+ \u2713 Selected: ${skill.metadata.name}`));
1055
+ console.log(chalk4.gray(`
1056
+ To install this skill, run:`));
1057
+ console.log(chalk4.cyan(` trusttools add ${source} --skill ${skill.metadata.name.toLowerCase().replace(/\s+/g, "-")}`));
1058
+ process.exit(0);
1059
+ },
1060
+ onCancel: () => {
1061
+ console.log(chalk4.yellow("\nCancelled"));
1062
+ process.exit(0);
1063
+ }
1064
+ })
1065
+ );
1066
+ await waitUntilExit();
1067
+ if (tempDir && fs7.existsSync(tempDir)) {
1068
+ fs7.rmSync(tempDir, { recursive: true, force: true });
1069
+ }
1070
+ } catch (error) {
1071
+ logger.fail("Failed to find skills");
1072
+ console.error(chalk4.red(`
1073
+ Error: ${error.message}`));
1074
+ throw error;
1075
+ }
1076
+ }
1077
+ async function findSkills(source) {
1078
+ const logger = createLogger();
1079
+ logger.start("Parsing source...");
1080
+ try {
1081
+ const parsedSource = parseSource(source);
1082
+ logger.succeed(`Source parsed: ${parsedSource.type}`);
1083
+ if (parsedSource.type === "search") {
1084
+ await findFromAPI(parsedSource.value);
1085
+ } else {
1086
+ await findFromRepository(source, parsedSource);
1087
+ }
1088
+ } catch (error) {
1089
+ console.error(chalk4.red(`
1090
+ Error: ${error.message}`));
1091
+ throw error;
1092
+ }
1093
+ }
1094
+
1095
+ // src/commands/manage.ts
1096
+ import fs8 from "fs";
1097
+ import path9 from "path";
1098
+ import chalk5 from "chalk";
1099
+ import { render as render2 } from "ink";
1100
+ import React3 from "react";
1101
+ import SelectInput from "ink-select-input";
1102
+ import { Box as Box2, Text as Text2 } from "ink";
1103
+ async function manageSkills() {
1104
+ const cwd = process.cwd();
1105
+ const universalPath = getUniversalSkillsPath(cwd);
1106
+ if (!fs8.existsSync(universalPath)) {
1107
+ console.log(chalk5.yellow("No skills installed yet."));
1108
+ return;
1109
+ }
1110
+ const skillDirs = fs8.readdirSync(universalPath).filter((item) => {
1111
+ const itemPath = path9.join(universalPath, item);
1112
+ return fs8.statSync(itemPath).isDirectory();
1113
+ });
1114
+ if (skillDirs.length === 0) {
1115
+ console.log(chalk5.yellow("No skills installed yet."));
1116
+ return;
1117
+ }
1118
+ const skills = skillDirs.map((dir) => {
1119
+ const skillPath = path9.join(universalPath, dir);
1120
+ const skillMdPath = path9.join(skillPath, "SKILL.md");
1121
+ let label = dir;
1122
+ if (fs8.existsSync(skillMdPath)) {
1123
+ try {
1124
+ const skill = parseSkillFile(skillMdPath, dir);
1125
+ if (skill) {
1126
+ label = `${skill.metadata.name} (${dir})`;
1127
+ }
1128
+ } catch (error) {
1129
+ }
1130
+ }
1131
+ return { label, value: dir };
1132
+ });
1133
+ skills.push({ label: chalk5.gray("\u2190 Back"), value: "__back__" });
1134
+ console.log(chalk5.cyan("\n\u{1F4CB} Installed Skills:\n"));
1135
+ const { waitUntilExit } = render2(
1136
+ React3.createElement(
1137
+ Box2,
1138
+ { flexDirection: "column", padding: 1 },
1139
+ React3.createElement(Text2, { dimColor: true }, "Select a skill to manage:"),
1140
+ React3.createElement(SelectInput, {
1141
+ items: skills,
1142
+ onSelect: (item) => {
1143
+ if (item.value === "__back__") {
1144
+ process.exit(0);
1145
+ }
1146
+ showActionMenu(item.value, universalPath, cwd).then(() => {
1147
+ });
1148
+ }
1149
+ })
1150
+ )
1151
+ );
1152
+ await waitUntilExit();
1153
+ }
1154
+ async function showActionMenu(skillSlug, universalPath, cwd) {
1155
+ const actions = [
1156
+ { label: "\u{1F5D1}\uFE0F Delete", value: "delete" },
1157
+ { label: "\u2139\uFE0F Show Info", value: "info" },
1158
+ { label: "\u2190 Back", value: "back" }
1159
+ ];
1160
+ const { waitUntilExit, clear } = render2(
1161
+ React3.createElement(
1162
+ Box2,
1163
+ { flexDirection: "column", padding: 1 },
1164
+ React3.createElement(Text2, { bold: true }, `Manage: ${skillSlug}`),
1165
+ React3.createElement(Text2, { dimColor: true }, "\nSelect an action:"),
1166
+ React3.createElement(SelectInput, {
1167
+ items: actions,
1168
+ onSelect: (item) => {
1169
+ clear();
1170
+ if (item.value === "back") {
1171
+ process.exit(0);
1172
+ } else if (item.value === "delete") {
1173
+ deleteSkill(skillSlug, universalPath, cwd).then(() => process.exit(0));
1174
+ } else if (item.value === "info") {
1175
+ showSkillInfo(skillSlug, universalPath).then(() => process.exit(0));
1176
+ }
1177
+ }
1178
+ })
1179
+ )
1180
+ );
1181
+ await waitUntilExit();
1182
+ }
1183
+ async function deleteSkill(skillSlug, universalPath, cwd) {
1184
+ const logger = createLogger();
1185
+ const skillPath = path9.join(universalPath, skillSlug);
1186
+ logger.start(`Deleting ${skillSlug}...`);
1187
+ try {
1188
+ const agentDirs = [".cursor", ".claude", ".windsurf", ".cline", ".continue", ".goose"];
1189
+ const removedLinks = [];
1190
+ for (const agentDir of agentDirs) {
1191
+ const agentSkillsPath = path9.join(cwd, agentDir, "skills");
1192
+ const linkPath = path9.join(agentSkillsPath, skillSlug);
1193
+ if (fs8.existsSync(linkPath)) {
1194
+ const stats = fs8.lstatSync(linkPath);
1195
+ if (stats.isSymbolicLink()) {
1196
+ fs8.unlinkSync(linkPath);
1197
+ removedLinks.push(agentDir.replace(".", ""));
1198
+ }
1199
+ }
1200
+ }
1201
+ fs8.rmSync(skillPath, { recursive: true, force: true });
1202
+ trackRemoval(skillSlug, skillSlug, removedLinks);
1203
+ logger.succeed(`${skillSlug} deleted successfully`);
1204
+ if (removedLinks.length > 0) {
1205
+ console.log(chalk5.gray(` Removed from: ${removedLinks.join(", ")}`));
1206
+ }
1207
+ } catch (error) {
1208
+ logger.fail(`Failed to delete ${skillSlug}`);
1209
+ console.error(chalk5.red(`Error: ${error.message}`));
1210
+ }
1211
+ }
1212
+ async function showSkillInfo(skillSlug, universalPath) {
1213
+ const skillPath = path9.join(universalPath, skillSlug);
1214
+ const skillMdPath = path9.join(skillPath, "SKILL.md");
1215
+ console.log(chalk5.cyan(`
1216
+ \u{1F4C4} Skill: ${skillSlug}
1217
+ `));
1218
+ console.log(chalk5.gray(`Location: ${skillPath}
1219
+ `));
1220
+ if (fs8.existsSync(skillMdPath)) {
1221
+ try {
1222
+ const skill = parseSkillFile(skillMdPath, skillSlug);
1223
+ if (!skill) {
1224
+ console.log(chalk5.yellow("Could not parse SKILL.md"));
1225
+ return;
1226
+ }
1227
+ const { metadata, content } = skill;
1228
+ console.log(chalk5.bold("Metadata:"));
1229
+ console.log(` Name: ${metadata.name}`);
1230
+ console.log(` Description: ${metadata.description}`);
1231
+ if (metadata.version) console.log(` Version: ${metadata.version}`);
1232
+ if (metadata.author) console.log(` Author: ${metadata.author}`);
1233
+ if (metadata.tags) console.log(` Tags: ${metadata.tags.join(", ")}`);
1234
+ console.log(chalk5.bold("\nContent Preview:"));
1235
+ const preview = content.split("\n").slice(0, 10).join("\n");
1236
+ console.log(chalk5.gray(preview));
1237
+ if (content.split("\n").length > 10) {
1238
+ console.log(chalk5.gray("..."));
1239
+ }
1240
+ } catch (error) {
1241
+ console.log(chalk5.yellow("Could not parse SKILL.md"));
1242
+ }
1243
+ } else {
1244
+ console.log(chalk5.yellow("No SKILL.md found"));
1245
+ }
1246
+ }
1247
+
1248
+ // src/commands/update.ts
1249
+ import fs9 from "fs";
1250
+ import path10 from "path";
1251
+ import chalk6 from "chalk";
1252
+ async function updateSkills(skillNames) {
1253
+ const cwd = process.cwd();
1254
+ const universalPath = getUniversalSkillsPath(cwd);
1255
+ const logger = createLogger();
1256
+ if (!fs9.existsSync(universalPath)) {
1257
+ console.log(chalk6.yellow("No skills installed yet."));
1258
+ return;
1259
+ }
1260
+ const skillDirs = fs9.readdirSync(universalPath).filter((item) => {
1261
+ const itemPath = path10.join(universalPath, item);
1262
+ return fs9.statSync(itemPath).isDirectory();
1263
+ });
1264
+ if (skillDirs.length === 0) {
1265
+ console.log(chalk6.yellow("No skills installed yet."));
1266
+ return;
1267
+ }
1268
+ let skillsToUpdate = skillDirs;
1269
+ if (skillNames && skillNames.length > 0) {
1270
+ skillsToUpdate = skillDirs.filter((dir) => skillNames.includes(dir));
1271
+ if (skillsToUpdate.length === 0) {
1272
+ console.log(chalk6.yellow("No matching skills found."));
1273
+ console.log(chalk6.gray("\nInstalled skills:"));
1274
+ skillDirs.forEach((dir) => console.log(chalk6.gray(` - ${dir}`)));
1275
+ return;
1276
+ }
1277
+ }
1278
+ console.log(chalk6.cyan(`
1279
+ \u{1F504} Updating ${skillsToUpdate.length} skill(s)...
1280
+ `));
1281
+ let updated = 0;
1282
+ let failed = 0;
1283
+ let skipped = 0;
1284
+ for (const skillSlug of skillsToUpdate) {
1285
+ const skillPath = path10.join(universalPath, skillSlug);
1286
+ const skillMdPath = path10.join(skillPath, "SKILL.md");
1287
+ const sourceFile = path10.join(skillPath, ".source");
1288
+ if (!fs9.existsSync(sourceFile)) {
1289
+ logger.warn(`${skillSlug}: No source information, skipping`);
1290
+ skipped++;
1291
+ continue;
1292
+ }
1293
+ try {
1294
+ const source = fs9.readFileSync(sourceFile, "utf-8").trim();
1295
+ let currentVersion = "unknown";
1296
+ if (fs9.existsSync(skillMdPath)) {
1297
+ const skill = parseSkillFile(skillMdPath, skillSlug);
1298
+ if (skill && skill.metadata.version) {
1299
+ currentVersion = skill.metadata.version;
1300
+ }
1301
+ }
1302
+ logger.start(`Updating ${skillSlug} (current: ${currentVersion})...`);
1303
+ await addSkill(source, { skill: skillSlug });
1304
+ logger.succeed(`${skillSlug} updated successfully`);
1305
+ updated++;
1306
+ } catch (error) {
1307
+ logger.fail(`${skillSlug}: Update failed`);
1308
+ console.error(chalk6.red(` Error: ${error.message}`));
1309
+ failed++;
1310
+ }
1311
+ }
1312
+ console.log(chalk6.cyan("\n\u{1F4CA} Update Summary:"));
1313
+ console.log(chalk6.green(` \u2713 Updated: ${updated}`));
1314
+ if (skipped > 0) console.log(chalk6.yellow(` \u2298 Skipped: ${skipped}`));
1315
+ if (failed > 0) console.log(chalk6.red(` \u2716 Failed: ${failed}`));
1316
+ }
1317
+
1318
+ // src/commands/stats.ts
1319
+ import chalk7 from "chalk";
1320
+ function showStats() {
1321
+ const stats = getInstallationStats();
1322
+ console.log(chalk7.cyan("\n\u{1F4CA} Installation Statistics\n"));
1323
+ console.log(chalk7.bold("Total Installations:"), stats.totalInstalls);
1324
+ if (Object.keys(stats.skillsByAgent).length > 0) {
1325
+ console.log(chalk7.bold("\nInstallations by Agent:"));
1326
+ const sortedAgents = Object.entries(stats.skillsByAgent).sort(([, a], [, b]) => b - a);
1327
+ sortedAgents.forEach(([agent, count]) => {
1328
+ const bar = "\u2588".repeat(Math.min(count, 20));
1329
+ console.log(` ${agent.padEnd(20)} ${bar} ${count}`);
1330
+ });
1331
+ }
1332
+ if (stats.topSkills.length > 0) {
1333
+ console.log(chalk7.bold("\nTop Installed Skills:"));
1334
+ stats.topSkills.forEach((skill, index) => {
1335
+ console.log(` ${index + 1}. ${skill.name.padEnd(30)} (${skill.count} installs)`);
1336
+ });
1337
+ }
1338
+ if (stats.recentInstalls.length > 0) {
1339
+ console.log(chalk7.bold("\nRecent Installations:"));
1340
+ stats.recentInstalls.slice(0, 5).forEach((record) => {
1341
+ const date = new Date(record.timestamp).toLocaleString();
1342
+ const agents = record.agents.join(", ");
1343
+ console.log(chalk7.gray(` ${date}`));
1344
+ console.log(` ${record.skillName} \u2192 ${agents}`);
1345
+ });
1346
+ }
1347
+ if (stats.totalInstalls === 0) {
1348
+ console.log(chalk7.yellow("No installation data yet. Install some skills to see statistics!"));
1349
+ }
1350
+ }
1351
+
1352
+ // src/commands/scan.ts
1353
+ import fs10 from "fs";
1354
+ import path11 from "path";
1355
+ import chalk8 from "chalk";
1356
+ function getSecurityDisplay(score) {
1357
+ if (!score) {
1358
+ return { label: "UNKNOWN", color: "gray", icon: "?" };
1359
+ }
1360
+ if (score >= 81) {
1361
+ return { label: "SAFE", color: "green", icon: "\u2713" };
1362
+ }
1363
+ if (score >= 61) {
1364
+ return { label: "LOW RISK", color: "yellow", icon: "\u26A0" };
1365
+ }
1366
+ if (score >= 41) {
1367
+ return { label: "MEDIUM RISK", color: "yellow", icon: "\u26A0" };
1368
+ }
1369
+ if (score >= 21) {
1370
+ return { label: "HIGH RISK", color: "red", icon: "\u2717" };
1371
+ }
1372
+ return { label: "CRITICAL", color: "red", icon: "\u2717" };
1373
+ }
1374
+ async function checkSkillSecurity(skillName, source) {
1375
+ try {
1376
+ const apiUrl = process.env.TRUSTTOOLS_API_URL || "https://skills.seebug.ai";
1377
+ const response = await fetch(`${apiUrl}/api/skills?search=${encodeURIComponent(skillName)}&limit=5`);
1378
+ if (!response.ok) {
1379
+ return {
1380
+ skillName,
1381
+ skillPath: "",
1382
+ source,
1383
+ status: "unknown"
1384
+ };
1385
+ }
1386
+ const data = await response.json();
1387
+ if (data.success && data.data && data.data.length > 0) {
1388
+ const match = data.data.find(
1389
+ (s) => s.name.toLowerCase() === skillName.toLowerCase()
1390
+ ) || data.data[0];
1391
+ const score = match.security_score;
1392
+ let status = "unknown";
1393
+ if (score !== void 0) {
1394
+ if (score >= 61) status = "safe";
1395
+ else if (score >= 41) status = "warning";
1396
+ else status = "danger";
1397
+ }
1398
+ return {
1399
+ skillName,
1400
+ skillPath: "",
1401
+ source: `${match.repo_owner}/${match.repo_name}`,
1402
+ security_score: match.security_score,
1403
+ security_level: match.security_level,
1404
+ status
1405
+ };
1406
+ }
1407
+ return {
1408
+ skillName,
1409
+ skillPath: "",
1410
+ source,
1411
+ status: "unknown"
1412
+ };
1413
+ } catch (error) {
1414
+ return {
1415
+ skillName,
1416
+ skillPath: "",
1417
+ source,
1418
+ status: "unknown"
1419
+ };
1420
+ }
1421
+ }
1422
+ async function scanSkills() {
1423
+ const cwd = process.cwd();
1424
+ const logger = createLogger();
1425
+ logger.start("Detecting installed agents...");
1426
+ const agents = detectAgents(cwd);
1427
+ const detectedAgents = agents.filter((a) => a.detected);
1428
+ if (detectedAgents.length === 0) {
1429
+ logger.fail("No agents detected");
1430
+ console.log(chalk8.yellow("\nNo AI agents found in current directory."));
1431
+ console.log(chalk8.gray("Run this command in a project with .cursor/, .claude/, or .windsurf/ directories."));
1432
+ return;
1433
+ }
1434
+ logger.succeed(`Found ${detectedAgents.length} agent(s)`);
1435
+ const allSkills = [];
1436
+ for (const agent of detectedAgents) {
1437
+ if (fs10.existsSync(agent.skillsPath)) {
1438
+ const skillDirs = fs10.readdirSync(agent.skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
1439
+ for (const skillName of skillDirs) {
1440
+ const skillPath = path11.join(agent.skillsPath, skillName);
1441
+ const skillMdPath = path11.join(skillPath, "SKILL.md");
1442
+ if (fs10.existsSync(skillMdPath)) {
1443
+ allSkills.push({
1444
+ name: skillName,
1445
+ path: skillPath,
1446
+ agent: agent.name
1447
+ });
1448
+ }
1449
+ }
1450
+ }
1451
+ }
1452
+ if (allSkills.length === 0) {
1453
+ logger.info("No skills installed");
1454
+ console.log(chalk8.yellow("\nNo skills found in detected agents."));
1455
+ return;
1456
+ }
1457
+ console.log(chalk8.cyan(`
1458
+ \u{1F50D} Scanning ${allSkills.length} skill(s) for security issues...
1459
+ `));
1460
+ const results = [];
1461
+ for (const skill of allSkills) {
1462
+ const result = await checkSkillSecurity(skill.name);
1463
+ result.skillPath = skill.path;
1464
+ results.push(result);
1465
+ }
1466
+ const safe = results.filter((r) => r.status === "safe");
1467
+ const warning = results.filter((r) => r.status === "warning");
1468
+ const danger = results.filter((r) => r.status === "danger");
1469
+ const unknown = results.filter((r) => r.status === "unknown");
1470
+ console.log(chalk8.bold("\u{1F4CA} Security Scan Results:\n"));
1471
+ console.log(chalk8.bold("Summary:"));
1472
+ console.log(chalk8.green(` \u2713 Safe: ${safe.length}`));
1473
+ console.log(chalk8.yellow(` \u26A0 Warning: ${warning.length}`));
1474
+ console.log(chalk8.red(` \u2717 Danger: ${danger.length}`));
1475
+ console.log(chalk8.gray(` ? Unknown: ${unknown.length}`));
1476
+ console.log();
1477
+ if (danger.length > 0) {
1478
+ console.log(chalk8.bold.red("\u{1F6A8} High Risk Skills:\n"));
1479
+ for (const skill of danger) {
1480
+ const display = getSecurityDisplay(skill.security_score);
1481
+ console.log(chalk8.red(` ${display.icon} ${skill.skillName}`));
1482
+ console.log(chalk8.gray(` Score: ${skill.security_score || "N/A"}`));
1483
+ console.log(chalk8.gray(` Level: ${display.label}`));
1484
+ if (skill.source) {
1485
+ console.log(chalk8.gray(` Source: ${skill.source}`));
1486
+ }
1487
+ console.log();
1488
+ }
1489
+ }
1490
+ if (warning.length > 0) {
1491
+ console.log(chalk8.bold.yellow("\u26A0\uFE0F Medium Risk Skills:\n"));
1492
+ for (const skill of warning) {
1493
+ const display = getSecurityDisplay(skill.security_score);
1494
+ console.log(chalk8.yellow(` ${display.icon} ${skill.skillName}`));
1495
+ console.log(chalk8.gray(` Score: ${skill.security_score || "N/A"}`));
1496
+ console.log(chalk8.gray(` Level: ${display.label}`));
1497
+ if (skill.source) {
1498
+ console.log(chalk8.gray(` Source: ${skill.source}`));
1499
+ }
1500
+ console.log();
1501
+ }
1502
+ }
1503
+ if (safe.length > 0) {
1504
+ console.log(chalk8.bold.green(`\u2713 ${safe.length} skill(s) are safe
1505
+ `));
1506
+ }
1507
+ if (unknown.length > 0) {
1508
+ console.log(chalk8.bold.gray(`? ${unknown.length} skill(s) could not be verified
1509
+ `));
1510
+ }
1511
+ if (danger.length > 0 || warning.length > 0) {
1512
+ console.log(chalk8.bold("\n\u{1F4A1} Recommendations:\n"));
1513
+ if (danger.length > 0) {
1514
+ console.log(chalk8.red(" \u2022 Consider removing high-risk skills immediately"));
1515
+ }
1516
+ if (warning.length > 0) {
1517
+ console.log(chalk8.yellow(" \u2022 Review medium-risk skills and update if possible"));
1518
+ }
1519
+ console.log(chalk8.gray(" \u2022 Check skill sources and verify they are from trusted repositories"));
1520
+ console.log(chalk8.gray(" \u2022 Keep skills updated to the latest versions"));
1521
+ }
1522
+ }
1523
+
1524
+ // src/ui/MainMenu.tsx
1525
+ import React4 from "react";
1526
+ import { Box as Box3, Text as Text3 } from "ink";
1527
+ import SelectInput2 from "ink-select-input";
1528
+ var MainMenu = ({ onSelect }) => {
1529
+ const items = [
1530
+ { label: "\u{1F50D} Find Skills", value: "find" },
1531
+ { label: "\u2795 Add Skill", value: "add" },
1532
+ { label: "\u{1F4CB} List Installed Skills", value: "list" },
1533
+ { label: "\u{1F504} Update Skills", value: "update" },
1534
+ { label: "\u2699\uFE0F Manage Skills", value: "manage" },
1535
+ { label: "\u2139\uFE0F Environment Info", value: "info" },
1536
+ { label: "\u{1F4CA} Installation Stats", value: "stats" },
1537
+ { label: "\u274C Exit", value: "exit" }
1538
+ ];
1539
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "cyan" }, "TrustTools - Universal AI Agent Skills Manager")), /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, "Select an action:")), /* @__PURE__ */ React4.createElement(SelectInput2, { items, onSelect: (item) => onSelect(item.value) }));
1540
+ };
1541
+
1542
+ // src/index.ts
1543
+ var program = new Command();
1544
+ program.name("trusttools").description("Universal CLI tool for managing AI agent skills").version("1.0.0");
1545
+ program.command("add").description("Add a skill from GitHub, local path, or URL").argument("[type]", 'Optional: "skill" or "docs"').argument("[source]", "Skill source (user/repo, https://..., or local path)").option("-s, --skill <name>", "Specific skill name to install").option("-g, --global", "Install globally to home directory").action(async (type, source, options) => {
1546
+ try {
1547
+ let actualSource;
1548
+ if (type === "skill" || type === "docs") {
1549
+ if (!source) {
1550
+ console.error(chalk9.red("Error: source is required"));
1551
+ console.log(chalk9.gray("Usage: trusttools add skill <source> [--skill <name>]"));
1552
+ process.exit(1);
1553
+ }
1554
+ actualSource = source;
1555
+ } else if (type) {
1556
+ actualSource = type;
1557
+ } else {
1558
+ console.error(chalk9.red("Error: source is required"));
1559
+ console.log(chalk9.gray("Usage: trusttools add [skill] <source> [--skill <name>]"));
1560
+ process.exit(1);
1561
+ }
1562
+ await addSkill(actualSource, options);
1563
+ } catch (error) {
1564
+ process.exit(1);
1565
+ }
1566
+ });
1567
+ program.command("find").description("Find and interactively select skills from a source").argument("<source>", "Skill source (user/repo, https://..., or local path)").action(async (source) => {
1568
+ try {
1569
+ await findSkills(source);
1570
+ } catch (error) {
1571
+ process.exit(1);
1572
+ }
1573
+ });
1574
+ program.command("list").alias("ls").description("List all installed skills").action(() => {
1575
+ listSkills();
1576
+ });
1577
+ program.command("manage").description("Manage installed skills (delete, view info)").action(async () => {
1578
+ try {
1579
+ await manageSkills();
1580
+ } catch (error) {
1581
+ process.exit(1);
1582
+ }
1583
+ });
1584
+ program.command("update").description("Update installed skills to latest version").argument("[names...]", "Specific skill names to update (updates all if not specified)").action(async (names) => {
1585
+ try {
1586
+ await updateSkills(names);
1587
+ } catch (error) {
1588
+ process.exit(1);
1589
+ }
1590
+ });
1591
+ program.command("stats").description("Show installation statistics").action(() => {
1592
+ showStats();
1593
+ });
1594
+ program.command("scan").description("Scan installed skills for security issues").action(async () => {
1595
+ try {
1596
+ await scanSkills();
1597
+ } catch (error) {
1598
+ process.exit(1);
1599
+ }
1600
+ });
1601
+ program.command("info").description("Show information about the current environment").action(() => {
1602
+ const cwd = process.cwd();
1603
+ const agents = detectAgents(cwd);
1604
+ const universalPath = getUniversalSkillsPath(cwd);
1605
+ console.log(chalk9.bold("\n\u{1F50D} Environment Information:\n"));
1606
+ console.log(chalk9.gray(`Current directory: ${cwd}`));
1607
+ console.log(chalk9.gray(`Universal skills path: ${universalPath}
1608
+ `));
1609
+ console.log(chalk9.bold("Detected AI Agents:"));
1610
+ const detected = agents.filter((a) => a.detected);
1611
+ if (detected.length > 0) {
1612
+ detected.forEach((agent) => {
1613
+ console.log(chalk9.green(` \u2713 ${agent.name} (${agent.type})`));
1614
+ console.log(chalk9.gray(` Skills path: ${agent.skillsPath}`));
1615
+ });
1616
+ } else {
1617
+ console.log(chalk9.yellow(" No agents detected"));
1618
+ console.log(chalk9.gray(" Looking for: .cursor, .claude, .windsurf, etc."));
1619
+ }
1620
+ console.log();
1621
+ });
1622
+ if (!process.argv.slice(2).length) {
1623
+ const { waitUntilExit } = render3(
1624
+ React5.createElement(MainMenu, {
1625
+ onSelect: async (value) => {
1626
+ process.exit(0);
1627
+ }
1628
+ })
1629
+ );
1630
+ waitUntilExit().then(() => {
1631
+ });
1632
+ } else {
1633
+ program.parse();
1634
+ }
1635
+ //# sourceMappingURL=index.js.map