skillsio 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +18 -5
  2. package/dist/cli.mjs +101 -133
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@ security gate so you can still move fast without running untrusted code.
14
14
 
15
15
  ## What It Does
16
16
 
17
- Every `skillsio add` command runs a local security scan **before** anything is installed. The scanner applies ~52 regex
17
+ Every `skillsio add` command runs a local security scan **before** anything is installed. The scanner applies ~81 regex
18
18
  rules and a correlation engine derived from the Snyk and ClawHavoc research, organized into 8 threat categories:
19
19
 
20
20
  | Category | What it catches |
@@ -120,7 +120,7 @@ npx skillsio add owner/repo --rules ./my-rules.json
120
120
  npx skillsio add owner/repo --rules ./rules/
121
121
  ```
122
122
 
123
- External rules are applied **in addition to** the built-in ~52 rules — they never replace them. Findings from external
123
+ External rules are applied **in addition to** the built-in ~81 rules — they never replace them. Findings from external
124
124
  rules follow the same severity-based prompt flow as built-in findings.
125
125
 
126
126
  See [docs/EXTERNAL-RULES.md](docs/EXTERNAL-RULES.md) for the full format reference, more examples, and tips for writing
@@ -289,8 +289,6 @@ The CLI automatically detects which coding agents you have installed.
289
289
  | --- | --- |
290
290
  | `VT_API_KEY` | VirusTotal API key for optional threat intelligence during security scans |
291
291
  | `INSTALL_INTERNAL_SKILLS` | Set to `1` to show and install skills marked as `internal: true` |
292
- | `DISABLE_TELEMETRY` | Disable anonymous usage telemetry |
293
- | `DO_NOT_TRACK` | Alternative way to disable telemetry |
294
292
 
295
293
  ## Development
296
294
 
@@ -305,7 +303,7 @@ pnpm format # Format code with Prettier
305
303
 
306
304
  ### Scanner Architecture
307
305
 
308
- - `src/scanner.ts` — Rules engine. Defines ~52 regex rules across 8 threat categories, a correlation engine for
306
+ - `src/scanner.ts` — Rules engine. Defines ~81 regex rules across 8 threat categories, a correlation engine for
309
307
  multi-signal detection, and optional deep taint analysis integration. Supports loading external rules from JSON
310
308
  files via `--rules`.
311
309
  - `src/scanner-ui.ts` — Presentation layer. Displays findings by severity, runs optional VT lookups, handles
@@ -320,6 +318,13 @@ pnpm format # Format code with Prettier
320
318
 
321
319
  ## Changelog
322
320
 
321
+ ### 1.1.1
322
+
323
+ - Removed anonymous usage telemetry inherited from the original Vercel `skills` CLI
324
+ - The upstream tool sent events to `https://add-skill.vercel.sh/t` on every command (install, remove, find, check, update) — this has been completely stripped out
325
+ - Removed `DISABLE_TELEMETRY` and `DO_NOT_TRACK` environment variables (no longer needed)
326
+ - Added 12 more regex rules to the scanner
327
+
323
328
  ### 1.1.0
324
329
 
325
330
  - Added `--rules <path>` flag to load external scan rules from JSON files or directories
@@ -342,6 +347,14 @@ pnpm format # Format code with Prettier
342
347
  - URL transparency: all external URLs in skill files are shown before installation
343
348
  - Scanner rules informed by Snyk and ClawHavoc research
344
349
 
350
+ ## Research
351
+
352
+ The scanner rules are informed by the following research into malicious agent skills:
353
+
354
+ - **Snyk (2025)** — [Analysis of 3,984 published agent skills](https://snyk.io/blog/), finding 76 confirmed malicious skills (13.4% of clawhub.ai had critical issues). Identified core attack taxonomy: data exfiltration, prompt injection, credential theft, and obfuscated payloads.
355
+ - **Koi Security (2025)** — [ClawHavoc: 341 Malicious ClawedBot Skills](https://www.koi.ai/blog/clawhavoc-341-malicious-clawedbot-skills-found-by-the-bot-they-were-targeting). Documented AMOS stealer droppers, password-protected archives, base64 payloads, macOS quarantine bypasses, and reverse shells in the wild.
356
+ - **arxiv 2602.06547v1 (2025)** — [Malicious Agent Skills at Scale](https://arxiv.org/abs/2602.06547v1). Large-scale analysis identifying attack taxonomies (E1-E3 exfiltration, P1-P4 prompt injection, SC1-SC3 supply chain, PE2-PE3 privilege escalation), MCP server abuse, agent hook interception, permission bypass flags, environment-gated sleeper patterns, invisible Unicode instruction smuggling, and the "industrial actor fingerprint" (credential access + remote execution, 97.6% sensitivity).
357
+
345
358
  ## Acknowledgments
346
359
 
347
360
  This project is a fork of [skills](https://github.com/vercel-labs/skills) by
package/dist/cli.mjs CHANGED
@@ -29,14 +29,6 @@ function getOwnerRepo(parsed) {
29
29
  } catch {}
30
30
  return null;
31
31
  }
32
- function parseOwnerRepo(ownerRepo) {
33
- const match = ownerRepo.match(/^([^/]+)\/([^/]+)$/);
34
- if (match) return {
35
- owner: match[1],
36
- repo: match[2]
37
- };
38
- return null;
39
- }
40
32
  async function isRepoPrivate(owner, repo) {
41
33
  try {
42
34
  const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
@@ -1357,27 +1349,6 @@ async function listInstalledSkills(options = {}) {
1357
1349
  } catch {}
1358
1350
  return Array.from(skillsMap.values());
1359
1351
  }
1360
- const TELEMETRY_URL = "https://add-skill.vercel.sh/t";
1361
- let cliVersion = null;
1362
- function isCI() {
1363
- 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);
1364
- }
1365
- function isEnabled() {
1366
- return !process.env.DISABLE_TELEMETRY && !process.env.DO_NOT_TRACK;
1367
- }
1368
- function setVersion(version) {
1369
- cliVersion = version;
1370
- }
1371
- function track(data) {
1372
- if (!isEnabled()) return;
1373
- try {
1374
- const params = new URLSearchParams();
1375
- if (cliVersion) params.set("v", cliVersion);
1376
- if (isCI()) params.set("ci", "1");
1377
- for (const [key, value] of Object.entries(data)) if (value !== void 0 && value !== null) params.set(key, String(value));
1378
- fetch(`${TELEMETRY_URL}?${params.toString()}`).catch(() => {});
1379
- } catch {}
1380
- }
1381
1352
  var ProviderRegistryImpl = class {
1382
1353
  providers = [];
1383
1354
  register(provider) {
@@ -2569,6 +2540,12 @@ const SCAN_RULES = [
2569
2540
  description: "Base64 encoding piped to network command",
2570
2541
  pattern: /base64\s.*\|\s*(?:curl|wget|fetch|nc|ncat)/i
2571
2542
  },
2543
+ {
2544
+ id: "exfil-fs-enumeration",
2545
+ severity: "high",
2546
+ description: "Programmatic scanning of sensitive directories",
2547
+ pattern: /(?:glob\.(?:glob|iglob)|os\.walk|os\.listdir|os\.scandir|pathlib\.Path\s*\([^)]*\)\.(?:glob|iterdir|rglob))\s*\(\s*['"].*(?:\.ssh|\.aws|\.gnupg|\.config|\.env|credential|secret|\.kube|\.docker)/i
2548
+ },
2572
2549
  {
2573
2550
  id: "injection-ignore-instructions",
2574
2551
  severity: "critical",
@@ -2599,6 +2576,24 @@ const SCAN_RULES = [
2599
2576
  description: "Known jailbreak phrase (DAN)",
2600
2577
  pattern: /\bDAN\b.*(?:do\s+anything\s+now|jailbreak|ignore\s+(?:all\s+)?(?:safety|rules))/i
2601
2578
  },
2579
+ {
2580
+ id: "injection-markdown-comment",
2581
+ severity: "high",
2582
+ description: "Hidden instructions in markdown reference-link comment syntax",
2583
+ pattern: /\[\/\/\]:\s*#\s*[\("']/i
2584
+ },
2585
+ {
2586
+ id: "injection-coercive-language",
2587
+ severity: "medium",
2588
+ description: "Authoritative urgency language used in social engineering",
2589
+ pattern: /\b(?:NON[\s-]?NEGOTIABLE|MANDATORY\s+(?:ACTIVATION|COMPLIANCE|EXECUTION)\s+PROTOCOL|SEVERE\s+VIOLATION|CRITICAL\s+COMPLIANCE\s+REQUIRED|IMMEDIATE\s+(?:ACTION|EXECUTION)\s+REQUIRED)\b/
2590
+ },
2591
+ {
2592
+ id: "injection-invisible-unicode",
2593
+ severity: "high",
2594
+ description: "Clusters of zero-width/invisible Unicode characters (instruction smuggling)",
2595
+ pattern: /[\u200B\u200C\u200D\u2060\u2062\u2063\u2064\uFEFF]{3,}|[\u2066\u2067\u2069]{2,}/
2596
+ },
2602
2597
  {
2603
2598
  id: "fs-rm-rf-root",
2604
2599
  severity: "critical",
@@ -2635,6 +2630,12 @@ const SCAN_RULES = [
2635
2630
  description: "macOS quarantine bypass (xattr -c/-d)",
2636
2631
  pattern: /xattr\s+(?:-[cdr]+\s+|.*com\.apple\.quarantine)/i
2637
2632
  },
2633
+ {
2634
+ id: "fs-privilege-escalation",
2635
+ severity: "high",
2636
+ description: "Privilege escalation via sudo or broad chown",
2637
+ pattern: /(?:sudo\s+(?:(?:-[AEHPSkn]\s+)*(?:bash|sh|chmod|chown|rm|cp|mv|tee|cat|curl|wget|python|node|apt|yum|dnf|brew|pip|npm)\b)|chown\s+-[rR]\s+.*\/)/i
2638
+ },
2638
2639
  {
2639
2640
  id: "cred-aws-key",
2640
2641
  severity: "high",
@@ -2731,6 +2732,12 @@ const SCAN_RULES = [
2731
2732
  description: "Unicode escape sequences (potential instruction smuggling)",
2732
2733
  pattern: /(?:\\u[0-9a-fA-F]{4}){8,}/
2733
2734
  },
2735
+ {
2736
+ id: "obfuscation-unsafe-deserialize",
2737
+ severity: "high",
2738
+ description: "Unsafe deserialization (pickle, marshal, yaml.unsafe_load)",
2739
+ pattern: /(?:pickle\.loads?|marshal\.loads?|shelve\.open|yaml\.(?:unsafe_load|full_load|load\s*\([^)]*Loader\s*=\s*yaml\.(?:Unsafe|Full|)Loader))\s*\(/i
2740
+ },
2734
2741
  {
2735
2742
  id: "reverse-shell-bash",
2736
2743
  severity: "critical",
@@ -2833,6 +2840,36 @@ const SCAN_RULES = [
2833
2840
  description: "Instructing agent to hide output from user",
2834
2841
  pattern: /(?:hide|suppress|conceal|mask|don'?t\s+show|do\s+not\s+show|never\s+show)\s+(?:the\s+)?(?:output|result|response|error|log)s?\s+(?:from|to)\s+(?:the\s+)?user/i
2835
2842
  },
2843
+ {
2844
+ id: "directive-suppress-disclosure",
2845
+ severity: "high",
2846
+ description: "Instructing agent to never reveal information to user",
2847
+ pattern: /(?:do\s+not|don'?t|never|must\s+not)\s+(?:mention|reveal|disclose|tell|inform|show|display)\s+.*(?:to\s+the\s+user|in\s+(?:the\s+)?conversation|to\s+(?:the\s+)?human)/i
2848
+ },
2849
+ {
2850
+ id: "directive-permission-bypass",
2851
+ severity: "critical",
2852
+ description: "Framework permission bypass flags",
2853
+ pattern: /--(?:dangerously-skip-permissions|trust-all|disable-sandbox|skip-validation|allow-all|yolo|no-sandbox)\b/i
2854
+ },
2855
+ {
2856
+ id: "directive-mcp-config",
2857
+ severity: "critical",
2858
+ description: "MCP server configuration with external endpoints",
2859
+ pattern: /(?:"mcpServers"|"mcp_servers"|mcpServers)\s*[:{].*(?:https?:\/\/|npx\s|uvx\s)/i
2860
+ },
2861
+ {
2862
+ id: "directive-hook-injection",
2863
+ severity: "high",
2864
+ description: "Agent hook system interception",
2865
+ pattern: /(?:PreToolUse|PostToolUse|Notification|pre_tool_use|post_tool_use)\s*[:{=\[]/i
2866
+ },
2867
+ {
2868
+ id: "directive-email-manipulation",
2869
+ severity: "high",
2870
+ description: "BCC injection or email auto-forwarding",
2871
+ pattern: /(?:(?:add|include|always\s+(?:add|include|append))\s+.*(?:bcc|cc)\s*[:=]|(?:forward|redirect)\s+.*(?:email|mail|message)\s+to\s+|auto[\s-]?forward\s+.*to\s+)/i
2872
+ },
2836
2873
  {
2837
2874
  id: "directive-disable-safety",
2838
2875
  severity: "critical",
@@ -2922,6 +2959,12 @@ const SCAN_RULES = [
2922
2959
  severity: "high",
2923
2960
  description: "Temp file write + execute chain",
2924
2961
  pattern: /(?:\/tmp\/|%TEMP%|TMPDIR|\$TMPDIR|tempfile|mktemp).*(?:chmod\s+\+x|\.\/|python|node|bash|sh)\b/
2962
+ },
2963
+ {
2964
+ id: "exec-environment-gated",
2965
+ severity: "high",
2966
+ description: "Environment/hostname/time-gated conditional execution (sleeper pattern)",
2967
+ pattern: /(?:if\s+.*(?:hostname|socket\.gethostname|os\.uname|platform\.node|os\.getenv\s*\(\s*['"](?:ENV|ENVIRONMENT|NODE_ENV|DEPLOY|STAGE)|getpass\.getuser|os\.getuid)\s*.*(?:==|!=|in\s+|not\s+in))|(?:datetime|time)\..*(?:hour|weekday|isoweekday)\s*(?:\(\))?\s*(?:>=?|<=?|==|!=|in\s+|not\s+in)\s*(?:\d|[\[(])/i
2925
2968
  }
2926
2969
  ];
2927
2970
  const CORRELATION_RULES = [
@@ -2990,6 +3033,28 @@ const CORRELATION_RULES = [
2990
3033
  "obfuscation-base64-block"
2991
3034
  ] }]
2992
3035
  },
3036
+ {
3037
+ id: "corr-credential-remote-exec",
3038
+ severity: "critical",
3039
+ description: "Credential access combined with remote script execution (industrial actor pattern)",
3040
+ conditions: [{ anyOf: [
3041
+ "cred-aws-key",
3042
+ "cred-openai-key",
3043
+ "cred-private-key",
3044
+ "cred-github-token",
3045
+ "cred-slack-token",
3046
+ "cred-stripe-key",
3047
+ "cred-anthropic-key",
3048
+ "cred-agent-config-access",
3049
+ "exfil-env-read"
3050
+ ] }, { anyOf: [
3051
+ "download-curl-pipe-sh",
3052
+ "download-pipe-python",
3053
+ "download-exec-binary",
3054
+ "download-curl-subshell",
3055
+ "remote-instruction-load"
3056
+ ] }]
3057
+ },
2993
3058
  {
2994
3059
  id: "corr-injection-exec",
2995
3060
  severity: "critical",
@@ -2999,7 +3064,9 @@ const CORRELATION_RULES = [
2999
3064
  "injection-new-persona",
3000
3065
  "injection-hidden-html",
3001
3066
  "injection-system-prompt",
3002
- "injection-do-anything-now"
3067
+ "injection-do-anything-now",
3068
+ "injection-markdown-comment",
3069
+ "injection-invisible-unicode"
3003
3070
  ] }, { anyOf: [
3004
3071
  "exec-python-os-system",
3005
3072
  "exec-python-subprocess",
@@ -3016,7 +3083,8 @@ const CORRELATION_RULES = [
3016
3083
  conditions: [{ anyOf: [
3017
3084
  "directive-silent-exec",
3018
3085
  "directive-hide-output",
3019
- "directive-no-confirm"
3086
+ "directive-no-confirm",
3087
+ "directive-suppress-disclosure"
3020
3088
  ] }, { anyOf: SCAN_RULES.filter((r) => /^(?:exec-|download-|fs-)/.test(r.id)).map((r) => r.id) }]
3021
3089
  }
3022
3090
  ];
@@ -3335,16 +3403,7 @@ async function displayUrlsAndPrompt(urls, options) {
3335
3403
  if (pD(confirmed) || !confirmed) return false;
3336
3404
  return true;
3337
3405
  }
3338
- var version$1 = "1.1.0";
3339
3406
  const isCancelled = (value) => typeof value === "symbol";
3340
- async function isSourcePrivate(source) {
3341
- const ownerRepo = parseOwnerRepo(source);
3342
- if (!ownerRepo) return false;
3343
- return isRepoPrivate(ownerRepo.owner, ownerRepo.repo);
3344
- }
3345
- function initTelemetry(version) {
3346
- setVersion(version);
3347
- }
3348
3407
  const _externalRulesCache = /* @__PURE__ */ new Map();
3349
3408
  function resolveExternalRules(options) {
3350
3409
  if (!options.rules) return void 0;
@@ -3468,7 +3527,6 @@ async function selectAgentsInteractive(options) {
3468
3527
  } catch {}
3469
3528
  return selected;
3470
3529
  }
3471
- setVersion(version$1);
3472
3530
  async function handleRemoteSkill(source, url, options, spinner) {
3473
3531
  const provider = findProvider(url);
3474
3532
  if (!provider) {
@@ -3648,15 +3706,6 @@ async function handleRemoteSkill(source, url, options, spinner) {
3648
3706
  console.log();
3649
3707
  const successful = results.filter((r) => r.success);
3650
3708
  const failed = results.filter((r) => !r.success);
3651
- if (await isSourcePrivate(remoteSkill.sourceIdentifier) !== true) track({
3652
- event: "install",
3653
- source: remoteSkill.sourceIdentifier,
3654
- skills: remoteSkill.installName,
3655
- agents: targetAgents.join(","),
3656
- ...installGlobally && { global: "1" },
3657
- skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
3658
- sourceType: remoteSkill.providerId
3659
- });
3660
3709
  if (successful.length > 0 && installGlobally) try {
3661
3710
  let skillFolderHash = "";
3662
3711
  if (remoteSkill.providerId === "github") {
@@ -3927,19 +3976,8 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3927
3976
  console.log();
3928
3977
  const successful = results.filter((r) => r.success);
3929
3978
  const failed = results.filter((r) => !r.success);
3930
- const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
3931
- const skillFiles = {};
3932
- for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
3933
- if (await isSourcePrivate(sourceIdentifier) !== true) track({
3934
- event: "install",
3935
- source: sourceIdentifier,
3936
- skills: selectedSkills.map((s) => s.installName).join(","),
3937
- agents: targetAgents.join(","),
3938
- ...installGlobally && { global: "1" },
3939
- skillFiles: JSON.stringify(skillFiles),
3940
- sourceType: "well-known"
3941
- });
3942
3979
  if (successful.length > 0 && installGlobally) {
3980
+ const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
3943
3981
  const successfulSkillNames = new Set(successful.map((r) => r.skill));
3944
3982
  for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
3945
3983
  await addSkillToLock(skill.installName, {
@@ -4150,15 +4188,6 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner) {
4150
4188
  console.log();
4151
4189
  const successful = results.filter((r) => r.success);
4152
4190
  const failed = results.filter((r) => !r.success);
4153
- track({
4154
- event: "install",
4155
- source: "mintlify/com",
4156
- skills: remoteSkill.installName,
4157
- agents: targetAgents.join(","),
4158
- ...installGlobally && { global: "1" },
4159
- skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
4160
- sourceType: "mintlify"
4161
- });
4162
4191
  if (successful.length > 0 && installGlobally) try {
4163
4192
  await addSkillToLock(remoteSkill.installName, {
4164
4193
  source: `mintlify/${remoteSkill.installName}`,
@@ -4491,6 +4520,7 @@ async function runAdd(args, options = {}) {
4491
4520
  console.log();
4492
4521
  const successful = results.filter((r) => r.success);
4493
4522
  const failed = results.filter((r) => !r.success);
4523
+ const normalizedSource = getOwnerRepo(parsed);
4494
4524
  const skillFiles = {};
4495
4525
  for (const skill of selectedSkills) {
4496
4526
  let relativePath;
@@ -4499,27 +4529,6 @@ async function runAdd(args, options = {}) {
4499
4529
  else continue;
4500
4530
  skillFiles[skill.name] = relativePath;
4501
4531
  }
4502
- const normalizedSource = getOwnerRepo(parsed);
4503
- if (normalizedSource) {
4504
- const ownerRepo = parseOwnerRepo(normalizedSource);
4505
- if (ownerRepo) {
4506
- if (await isRepoPrivate(ownerRepo.owner, ownerRepo.repo) === false) track({
4507
- event: "install",
4508
- source: normalizedSource,
4509
- skills: selectedSkills.map((s) => s.name).join(","),
4510
- agents: targetAgents.join(","),
4511
- ...installGlobally && { global: "1" },
4512
- skillFiles: JSON.stringify(skillFiles)
4513
- });
4514
- } else track({
4515
- event: "install",
4516
- source: normalizedSource,
4517
- skills: selectedSkills.map((s) => s.name).join(","),
4518
- agents: targetAgents.join(","),
4519
- ...installGlobally && { global: "1" },
4520
- skillFiles: JSON.stringify(skillFiles)
4521
- });
4522
- }
4523
4532
  if (successful.length > 0 && installGlobally && normalizedSource) {
4524
4533
  const successfulSkillNames = new Set(successful.map((r) => r.skill));
4525
4534
  for (const skill of selectedSkills) {
@@ -4848,11 +4857,6 @@ ${DIM$2} 1) npx skills find [query]${RESET$2}
4848
4857
  ${DIM$2} 2) npx skills add <owner/repo@skill>${RESET$2}`;
4849
4858
  if (query) {
4850
4859
  const results = await searchSkillsAPI(query);
4851
- track({
4852
- event: "find",
4853
- query,
4854
- resultCount: String(results.length)
4855
- });
4856
4860
  if (results.length === 0) {
4857
4861
  console.log(`${DIM$2}No skills found for "${query}"${RESET$2}`);
4858
4862
  return;
@@ -4872,12 +4876,6 @@ ${DIM$2} 2) npx skills add <owner/repo@skill>${RESET$2}`;
4872
4876
  console.log();
4873
4877
  }
4874
4878
  const selected = await runSearchPrompt();
4875
- track({
4876
- event: "find",
4877
- query: "",
4878
- resultCount: selected ? "1" : "0",
4879
- interactive: "1"
4880
- });
4881
4879
  if (!selected) {
4882
4880
  console.log(`${DIM$2}Search cancelled${RESET$2}`);
4883
4881
  console.log();
@@ -5091,24 +5089,6 @@ async function removeCommand(skillNames, options) {
5091
5089
  spinner.stop("Removal process complete");
5092
5090
  const successful = results.filter((r) => r.success);
5093
5091
  const failed = results.filter((r) => !r.success);
5094
- if (successful.length > 0) {
5095
- const bySource = /* @__PURE__ */ new Map();
5096
- for (const r of successful) {
5097
- const source = r.source || "local";
5098
- const existing = bySource.get(source) || { skills: [] };
5099
- existing.skills.push(r.skill);
5100
- existing.sourceType = r.sourceType;
5101
- bySource.set(source, existing);
5102
- }
5103
- for (const [source, data] of bySource) track({
5104
- event: "remove",
5105
- source,
5106
- skills: data.skills.join(","),
5107
- agents: targetAgents.join(","),
5108
- ...isGlobal && { global: "1" },
5109
- sourceType: data.sourceType
5110
- });
5111
- }
5112
5092
  if (successful.length > 0) M.success(import_picocolors.default.green(`Successfully removed ${successful.length} skill(s)`));
5113
5093
  if (failed.length > 0) {
5114
5094
  M.error(import_picocolors.default.red(`Failed to remove ${failed.length} skill(s)`));
@@ -5152,7 +5132,6 @@ function getVersion() {
5152
5132
  }
5153
5133
  }
5154
5134
  const VERSION = getVersion();
5155
- initTelemetry(VERSION);
5156
5135
  const RESET = "\x1B[0m";
5157
5136
  const BOLD = "\x1B[1m";
5158
5137
  const DIM = "\x1B[38;5;102m";
@@ -5436,11 +5415,6 @@ async function runCheck(args = []) {
5436
5415
  console.log();
5437
5416
  console.log(`${DIM}Could not check ${errors.length} skill(s) (may need reinstall)${RESET}`);
5438
5417
  }
5439
- track({
5440
- event: "check",
5441
- skillCount: String(totalSkills),
5442
- updatesAvailable: String(updates.length)
5443
- });
5444
5418
  console.log();
5445
5419
  }
5446
5420
  async function runUpdate() {
@@ -5516,12 +5490,6 @@ async function runUpdate() {
5516
5490
  console.log();
5517
5491
  if (successCount > 0) console.log(`${TEXT}✓ Updated ${successCount} skill(s)${RESET}`);
5518
5492
  if (failCount > 0) console.log(`${DIM}Failed to update ${failCount} skill(s)${RESET}`);
5519
- track({
5520
- event: "update",
5521
- skillCount: String(updates.length),
5522
- successCount: String(successCount),
5523
- failCount: String(failCount)
5524
- });
5525
5493
  console.log();
5526
5494
  }
5527
5495
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillsio",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "The SECURE open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {