skillfish 1.0.25 → 1.0.27

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.
@@ -7,7 +7,7 @@ import { dirname, basename } from 'path';
7
7
  import * as p from '@clack/prompts';
8
8
  import pc from 'picocolors';
9
9
  import { printBanner } from '../lib/banner.js';
10
- import { trackInstall } from '../telemetry.js';
10
+ import { trackCommand, trackInstall } from '../telemetry.js';
11
11
  import { isValidPath, parseFrontmatter, deriveSkillName, toTitleCase, truncate, batchMap, createJsonOutput, isInputTTY, isTTY, } from '../utils.js';
12
12
  import { getDetectedAgentsForLocation, AGENT_CONFIGS, } from '../lib/agents.js';
13
13
  import { findAllSkillMdFiles, fetchSkillMdContent, fetchDefaultBranch, fetchTreeSha, getSkillSha, SKILL_FILENAME, RateLimitError, RepoNotFoundError, NetworkError, GitHubApiError, } from '../lib/github.js';
@@ -71,6 +71,8 @@ Examples:
71
71
  printBanner();
72
72
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
73
73
  }
74
+ // Track command usage (fire and forget)
75
+ void trackCommand('add');
74
76
  const force = options.force ?? false;
75
77
  const trustSource = options.yes ?? false;
76
78
  const installAll = options.all ?? false;
@@ -185,7 +187,6 @@ Examples:
185
187
  // Install each selected skill
186
188
  let totalInstalled = 0;
187
189
  let totalSkipped = 0;
188
- const telemetryPromises = [];
189
190
  // SECURITY: Ask for confirmation before installation (unless --yes is used)
190
191
  // Single confirmation for all selected skills
191
192
  if (!trustSource && !jsonMode && isInputTTY()) {
@@ -262,15 +263,11 @@ Examples:
262
263
  }
263
264
  totalInstalled += result.installed.length;
264
265
  totalSkipped += result.skipped.length;
265
- // Track successful installs (telemetry with timeout)
266
+ // Track successful installs (fire and forget)
266
267
  if (result.installed.length > 0) {
267
- telemetryPromises.push(trackInstall(owner, repo, skillName));
268
+ void trackInstall('add', owner, repo, skillName);
268
269
  }
269
270
  }
270
- // Wait for telemetry to complete (with timeout built into trackInstall)
271
- if (telemetryPromises.length > 0) {
272
- await Promise.all(telemetryPromises);
273
- }
274
271
  // Summary
275
272
  if (jsonMode) {
276
273
  outputJsonAndExit(EXIT_CODES.SUCCESS);
@@ -7,6 +7,7 @@ import { join } from 'path';
7
7
  import * as p from '@clack/prompts';
8
8
  import pc from 'picocolors';
9
9
  import { printBanner } from '../lib/banner.js';
10
+ import { trackCommand } from '../telemetry.js';
10
11
  import { getDetectedAgentsForLocation, getAgentSkillDir } from '../lib/agents.js';
11
12
  import { listInstalledSkillsInDir } from '../lib/installer.js';
12
13
  import { readManifest, hasManifest, healManifest, writeManifest, MANIFEST_VERSION, } from '../lib/manifest.js';
@@ -76,6 +77,8 @@ Examples:
76
77
  printBanner();
77
78
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
78
79
  }
80
+ // Track command usage (fire and forget)
81
+ void trackCommand('bundle');
79
82
  // Determine scope (interactive if no flags specified)
80
83
  const { baseDir, location } = await selectBundleLocation(projectFlag, globalFlag, jsonMode);
81
84
  const manifestPath = getProjectManifestPath(location === 'global');
@@ -8,6 +8,7 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
8
8
  import * as p from '@clack/prompts';
9
9
  import pc from 'picocolors';
10
10
  import { printBanner } from '../lib/banner.js';
11
+ import { trackCommand } from '../telemetry.js';
11
12
  import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
12
13
  import { SKILL_FILENAME } from '../lib/github.js';
13
14
  import { EXIT_CODES } from '../lib/constants.js';
@@ -159,6 +160,8 @@ Examples:
159
160
  printBanner();
160
161
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)} ${pc.dim('· Create a skill')}`);
161
162
  }
163
+ // Track command usage (fire and forget)
164
+ void trackCommand('init');
162
165
  const skipPrompts = options.yes ?? false;
163
166
  const projectFlag = options.project ?? false;
164
167
  const globalFlag = options.global ?? false;
@@ -8,6 +8,7 @@ import { join } from 'path';
8
8
  import * as p from '@clack/prompts';
9
9
  import pc from 'picocolors';
10
10
  import { printBanner } from '../lib/banner.js';
11
+ import { trackCommand, trackInstall } from '../telemetry.js';
11
12
  import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
12
13
  import { installSkill, listInstalledSkillsInDir } from '../lib/installer.js';
13
14
  import { readManifest, getManifestKey, buildManifestKey, } from '../lib/manifest.js';
@@ -85,6 +86,8 @@ Examples:
85
86
  printBanner();
86
87
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
87
88
  }
89
+ // Track command usage (fire and forget)
90
+ void trackCommand('install');
88
91
  // Determine scope (interactive if no flags specified)
89
92
  const { location, baseDir, manifestPath } = await selectInstallLocation(projectFlag, globalFlag, jsonMode);
90
93
  jsonOutput.manifest_path = manifestPath;
@@ -531,6 +534,8 @@ Examples:
531
534
  const action = toInstall[i];
532
535
  if (result.success) {
533
536
  successCount++;
537
+ // Track successful installs (fire and forget)
538
+ void trackInstall('install', action.entry.owner, action.entry.repo, result.skillName);
534
539
  if (!jsonMode) {
535
540
  // Show which agents it was installed to if it's a partial install
536
541
  const agentCount = action.targetAgents.length;
@@ -7,6 +7,7 @@ import { join } from 'path';
7
7
  import * as p from '@clack/prompts';
8
8
  import pc from 'picocolors';
9
9
  import { printBanner } from '../lib/banner.js';
10
+ import { trackCommand } from '../telemetry.js';
10
11
  import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
11
12
  import { listInstalledSkillsInDir } from '../lib/installer.js';
12
13
  import { EXIT_CODES } from '../lib/constants.js';
@@ -63,6 +64,8 @@ Examples:
63
64
  agents_detected: [],
64
65
  });
65
66
  }
67
+ // Track command usage (fire and forget)
68
+ void trackCommand('list');
66
69
  // Determine which locations to check
67
70
  // By default, check both global and project. Flags narrow it down.
68
71
  const checkGlobal = !projectFlag; // Check global unless --project is set
@@ -8,6 +8,7 @@ import { existsSync, rmSync } from 'fs';
8
8
  import * as p from '@clack/prompts';
9
9
  import pc from 'picocolors';
10
10
  import { printBanner } from '../lib/banner.js';
11
+ import { trackCommand } from '../telemetry.js';
11
12
  import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
12
13
  import { listInstalledSkillsInDir } from '../lib/installer.js';
13
14
  import { readManifest } from '../lib/manifest.js';
@@ -68,6 +69,8 @@ Examples:
68
69
  printBanner();
69
70
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
70
71
  }
72
+ // Track command usage (fire and forget)
73
+ void trackCommand('remove');
71
74
  const skipConfirm = options.yes ?? false;
72
75
  const removeAll = options.all ?? false;
73
76
  const projectFlag = options.project ?? false;
@@ -5,6 +5,7 @@ import { Command } from 'commander';
5
5
  import * as p from '@clack/prompts';
6
6
  import pc from 'picocolors';
7
7
  import { printBanner } from '../lib/banner.js';
8
+ import { trackCommand } from '../telemetry.js';
8
9
  import { searchSkillsInRegistry } from '../lib/registry.js';
9
10
  import { EXIT_CODES } from '../lib/constants.js';
10
11
  import { isTTY, truncate } from '../utils.js';
@@ -69,6 +70,8 @@ Examples:
69
70
  printBanner();
70
71
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim('Search')}`);
71
72
  }
73
+ // Track command usage (fire and forget)
74
+ void trackCommand('search');
72
75
  // Show spinner while searching
73
76
  let response;
74
77
  if (!jsonMode) {
@@ -5,6 +5,7 @@ import { Command } from 'commander';
5
5
  import { dirname, basename } from 'path';
6
6
  import * as p from '@clack/prompts';
7
7
  import pc from 'picocolors';
8
+ import { trackCommand } from '../telemetry.js';
8
9
  import { parseFrontmatter, batchMap, isInputTTY, isTTY, createSubmitJsonOutput, } from '../utils.js';
9
10
  import { findAllSkillMdFiles, fetchSkillMdContent, SKILL_FILENAME, RateLimitError, RepoNotFoundError, NetworkError, GitHubApiError, } from '../lib/github.js';
10
11
  import { EXIT_CODES, isValidName } from '../lib/constants.js';
@@ -59,6 +60,8 @@ Examples:
59
60
  console.log();
60
61
  p.intro(`${pc.bgCyan(pc.black(' skillfish submit '))} ${pc.dim(`v${version}`)}`);
61
62
  }
63
+ // Track command usage (fire and forget)
64
+ void trackCommand('submit');
62
65
  const skipConfirm = options.yes ?? false;
63
66
  // Parse repo format - supports owner/repo or full GitHub URL
64
67
  let owner;
@@ -7,6 +7,7 @@ import { join } from 'path';
7
7
  import * as p from '@clack/prompts';
8
8
  import pc from 'picocolors';
9
9
  import { printBanner } from '../lib/banner.js';
10
+ import { trackCommand } from '../telemetry.js';
10
11
  import { getDetectedAgentsForLocation, getAgentSkillDir } from '../lib/agents.js';
11
12
  import { listInstalledSkillsInDir, installSkill } from '../lib/installer.js';
12
13
  import { readManifest } from '../lib/manifest.js';
@@ -58,6 +59,8 @@ Examples:
58
59
  printBanner();
59
60
  p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
60
61
  }
62
+ // Track command usage (fire and forget)
63
+ void trackCommand('update');
61
64
  // Detect agents (check both global and project for updates)
62
65
  const detected = getDetectedAgentsForLocation('both', process.cwd());
63
66
  if (detected.length === 0) {
@@ -1,10 +1,17 @@
1
1
  /**
2
- * Track a skill install. Returns a promise that resolves when the request
3
- * completes (or times out). Never rejects.
2
+ * Track a command execution. Fire and forget.
4
3
  *
4
+ * @param command The command name (e.g., 'add', 'bundle', 'install')
5
+ * @returns Promise that resolves when telemetry is sent (or times out)
6
+ */
7
+ export declare function trackCommand(command: string): Promise<void>;
8
+ /**
9
+ * Track a skill install. Inserts into telemetry_events and increments skill download count.
10
+ *
11
+ * @param command The command that triggered the install ('add' or 'install')
5
12
  * @param owner GitHub repository owner
6
13
  * @param repo GitHub repository name
7
14
  * @param skillName Name of the skill being installed
8
15
  * @returns Promise that resolves when telemetry is sent (or times out)
9
16
  */
10
- export declare function trackInstall(owner: string, repo: string, skillName: string): Promise<void>;
17
+ export declare function trackInstall(command: string, owner: string, repo: string, skillName: string): Promise<void>;
package/dist/telemetry.js CHANGED
@@ -2,31 +2,20 @@ const TELEMETRY_URL = 'https://mcpmarket.com/api/telemetry';
2
2
  /** Timeout for telemetry requests (ms) */
3
3
  const TELEMETRY_TIMEOUT = 5000;
4
4
  /**
5
- * Track a skill install. Returns a promise that resolves when the request
5
+ * Send a telemetry payload. Returns a promise that resolves when the request
6
6
  * completes (or times out). Never rejects.
7
- *
8
- * @param owner GitHub repository owner
9
- * @param repo GitHub repository name
10
- * @param skillName Name of the skill being installed
11
- * @returns Promise that resolves when telemetry is sent (or times out)
12
7
  */
13
- export function trackInstall(owner, repo, skillName) {
8
+ function sendTelemetry(payload) {
14
9
  try {
15
10
  if (process.env.DO_NOT_TRACK === '1' || process.env.CI === 'true') {
16
11
  return Promise.resolve();
17
12
  }
18
- if (!owner || !repo || !skillName) {
19
- return Promise.resolve();
20
- }
21
- // Use AbortController to properly timeout the request
22
- // This ensures the fetch actually completes (or aborts) before we return
23
13
  const controller = new AbortController();
24
14
  const timeoutId = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT);
25
- const body = JSON.stringify({ owner, repo, skillName });
26
15
  return fetch(TELEMETRY_URL, {
27
16
  method: 'POST',
28
17
  headers: { 'Content-Type': 'application/json' },
29
- body,
18
+ body: JSON.stringify(payload),
30
19
  signal: controller.signal,
31
20
  })
32
21
  .then(() => { })
@@ -37,3 +26,36 @@ export function trackInstall(owner, repo, skillName) {
37
26
  return Promise.resolve();
38
27
  }
39
28
  }
29
+ /**
30
+ * Track a command execution. Fire and forget.
31
+ *
32
+ * @param command The command name (e.g., 'add', 'bundle', 'install')
33
+ * @returns Promise that resolves when telemetry is sent (or times out)
34
+ */
35
+ export function trackCommand(command) {
36
+ if (!command)
37
+ return Promise.resolve();
38
+ return sendTelemetry({ type: 'command', command });
39
+ }
40
+ /**
41
+ * Track a skill install. Inserts into telemetry_events and increments skill download count.
42
+ *
43
+ * @param command The command that triggered the install ('add' or 'install')
44
+ * @param owner GitHub repository owner
45
+ * @param repo GitHub repository name
46
+ * @param skillName Name of the skill being installed
47
+ * @returns Promise that resolves when telemetry is sent (or times out)
48
+ */
49
+ export function trackInstall(command, owner, repo, skillName) {
50
+ if (!command || !owner || !repo || !skillName)
51
+ return Promise.resolve();
52
+ return sendTelemetry({
53
+ type: 'install',
54
+ command,
55
+ skillKey: `${owner}/${repo}`,
56
+ // Legacy fields for skill count increment
57
+ owner,
58
+ repo,
59
+ skillName,
60
+ });
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfish",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "All in one Skill manager for AI coding agents. Install, update, and sync Skills across Claude Code, Cursor, Copilot + more.",
5
5
  "type": "module",
6
6
  "bin": {