@mycelish/cli 0.2.7 → 0.3.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/commands/disable.test.js +0 -20
- package/dist/commands/disable.test.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +65 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enable.test.js +0 -18
- package/dist/commands/enable.test.js.map +1 -1
- package/dist/commands/health-checks/runner.d.ts.map +1 -1
- package/dist/commands/health-checks/runner.js +23 -1
- package/dist/commands/health-checks/runner.js.map +1 -1
- package/dist/commands/marketplace.d.ts.map +1 -1
- package/dist/commands/marketplace.js +34 -1
- package/dist/commands/marketplace.js.map +1 -1
- package/dist/commands/remote.d.ts.map +1 -1
- package/dist/commands/remote.js +17 -0
- package/dist/commands/remote.js.map +1 -1
- package/dist/commands/remove.d.ts +16 -7
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +234 -30
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/remove.test.js +18 -0
- package/dist/commands/remove.test.js.map +1 -1
- package/dist/commands/status.test.js +6 -1
- package/dist/commands/status.test.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +20 -1
- package/dist/commands/sync.js.map +1 -1
- package/dist/core/auto-adapter.d.ts.map +1 -1
- package/dist/core/auto-adapter.js +2 -0
- package/dist/core/auto-adapter.js.map +1 -1
- package/dist/core/config-merger.d.ts.map +1 -1
- package/dist/core/config-merger.js +39 -6
- package/dist/core/config-merger.js.map +1 -1
- package/dist/core/config-merger.test.js +28 -0
- package/dist/core/config-merger.test.js.map +1 -1
- package/dist/core/content-hash.d.ts +3 -0
- package/dist/core/content-hash.d.ts.map +1 -0
- package/dist/core/content-hash.js +11 -0
- package/dist/core/content-hash.js.map +1 -0
- package/dist/core/env-template.d.ts +8 -0
- package/dist/core/env-template.d.ts.map +1 -1
- package/dist/core/env-template.js +67 -1
- package/dist/core/env-template.js.map +1 -1
- package/dist/core/env-template.test.js +100 -1
- package/dist/core/env-template.test.js.map +1 -1
- package/dist/core/manifest-migrator.d.ts +0 -1
- package/dist/core/manifest-migrator.d.ts.map +1 -1
- package/dist/core/manifest-migrator.js +1 -1
- package/dist/core/manifest-migrator.js.map +1 -1
- package/dist/core/manifest-state.d.ts +8 -2
- package/dist/core/manifest-state.d.ts.map +1 -1
- package/dist/core/manifest-state.js +32 -1
- package/dist/core/manifest-state.js.map +1 -1
- package/dist/core/manifest-state.test.js +3 -7
- package/dist/core/manifest-state.test.js.map +1 -1
- package/dist/core/marketplace-cache.d.ts +12 -0
- package/dist/core/marketplace-cache.d.ts.map +1 -0
- package/dist/core/marketplace-cache.js +127 -0
- package/dist/core/marketplace-cache.js.map +1 -0
- package/dist/core/marketplace-cache.test.d.ts +2 -0
- package/dist/core/marketplace-cache.test.d.ts.map +1 -0
- package/dist/core/marketplace-cache.test.js +122 -0
- package/dist/core/marketplace-cache.test.js.map +1 -0
- package/dist/core/marketplace-constants.d.ts +14 -0
- package/dist/core/marketplace-constants.d.ts.map +1 -0
- package/dist/core/marketplace-constants.js +14 -0
- package/dist/core/marketplace-constants.js.map +1 -0
- package/dist/core/marketplace-deduplicator.d.ts +13 -0
- package/dist/core/marketplace-deduplicator.d.ts.map +1 -0
- package/dist/core/marketplace-deduplicator.js +84 -0
- package/dist/core/marketplace-deduplicator.js.map +1 -0
- package/dist/core/marketplace-deduplicator.test.d.ts +2 -0
- package/dist/core/marketplace-deduplicator.test.d.ts.map +1 -0
- package/dist/core/marketplace-deduplicator.test.js +70 -0
- package/dist/core/marketplace-deduplicator.test.js.map +1 -0
- package/dist/core/marketplace-registry.d.ts.map +1 -1
- package/dist/core/marketplace-registry.js +10 -16
- package/dist/core/marketplace-registry.js.map +1 -1
- package/dist/core/marketplace-registry.test.js +5 -4
- package/dist/core/marketplace-registry.test.js.map +1 -1
- package/dist/core/marketplace-sources.d.ts +68 -15
- package/dist/core/marketplace-sources.d.ts.map +1 -1
- package/dist/core/marketplace-sources.js +415 -91
- package/dist/core/marketplace-sources.js.map +1 -1
- package/dist/core/marketplace-sources.test.d.ts +2 -0
- package/dist/core/marketplace-sources.test.d.ts.map +1 -0
- package/dist/core/marketplace-sources.test.js +240 -0
- package/dist/core/marketplace-sources.test.js.map +1 -0
- package/dist/core/marketplace.d.ts +22 -5
- package/dist/core/marketplace.d.ts.map +1 -1
- package/dist/core/marketplace.js +371 -120
- package/dist/core/marketplace.js.map +1 -1
- package/dist/core/marketplace.test.js +90 -39
- package/dist/core/marketplace.test.js.map +1 -1
- package/dist/core/migrator/executor.d.ts.map +1 -1
- package/dist/core/migrator/executor.js +11 -2
- package/dist/core/migrator/executor.js.map +1 -1
- package/dist/core/migrator/manifest.d.ts +10 -1
- package/dist/core/migrator/manifest.d.ts.map +1 -1
- package/dist/core/migrator/manifest.js +12 -2
- package/dist/core/migrator/manifest.js.map +1 -1
- package/dist/core/plugin-scanner.test.js +0 -4
- package/dist/core/plugin-scanner.test.js.map +1 -1
- package/dist/core/plugin-state.d.ts.map +1 -1
- package/dist/core/plugin-state.js +50 -18
- package/dist/core/plugin-state.js.map +1 -1
- package/dist/core/plugin-takeover.d.ts +15 -0
- package/dist/core/plugin-takeover.d.ts.map +1 -1
- package/dist/core/plugin-takeover.js +163 -3
- package/dist/core/plugin-takeover.js.map +1 -1
- package/dist/core/presets.test.js +0 -3
- package/dist/core/presets.test.js.map +1 -1
- package/dist/core/secret-detector.d.ts +20 -0
- package/dist/core/secret-detector.d.ts.map +1 -0
- package/dist/core/secret-detector.js +83 -0
- package/dist/core/secret-detector.js.map +1 -0
- package/dist/core/secret-detector.test.d.ts +2 -0
- package/dist/core/secret-detector.test.d.ts.map +1 -0
- package/dist/core/secret-detector.test.js +91 -0
- package/dist/core/secret-detector.test.js.map +1 -0
- package/dist/core/security-scanner.d.ts +24 -0
- package/dist/core/security-scanner.d.ts.map +1 -0
- package/dist/core/security-scanner.js +182 -0
- package/dist/core/security-scanner.js.map +1 -0
- package/dist/core/security-scanner.test.d.ts +2 -0
- package/dist/core/security-scanner.test.d.ts.map +1 -0
- package/dist/core/security-scanner.test.js +120 -0
- package/dist/core/security-scanner.test.js.map +1 -0
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/snapshot.js +0 -6
- package/dist/core/snapshot.js.map +1 -1
- package/dist/core/snapshot.test.js +0 -4
- package/dist/core/snapshot.test.js.map +1 -1
- package/dist/core/state-verifier.d.ts.map +1 -1
- package/dist/core/state-verifier.js +0 -1
- package/dist/core/state-verifier.js.map +1 -1
- package/dist/dashboard/assets/_baseUniq-CaVE7eUV.js +1 -0
- package/dist/dashboard/assets/arc-CR894Erh.js +1 -0
- package/dist/dashboard/assets/architectureDiagram-VXUJARFQ-vuLSFK92.js +36 -0
- package/dist/dashboard/assets/blockDiagram-VD42YOAC-CjXwNwFV.js +122 -0
- package/dist/dashboard/assets/c4Diagram-YG6GDRKO-BUk0KT3V.js +10 -0
- package/dist/dashboard/assets/channel-BzUEK7Iv.js +1 -0
- package/dist/dashboard/assets/chunk-4BX2VUAB-BCCD1RD0.js +1 -0
- package/dist/dashboard/assets/chunk-55IACEB6-7M5H4j_M.js +1 -0
- package/dist/dashboard/assets/chunk-B4BG7PRW-CkuZp9sw.js +165 -0
- package/dist/dashboard/assets/chunk-DI55MBZ5-aoWuqaxJ.js +220 -0
- package/dist/dashboard/assets/chunk-FMBD7UC4-zGR2QW6c.js +15 -0
- package/dist/dashboard/assets/chunk-QN33PNHL-Lj8Zu7hm.js +1 -0
- package/dist/dashboard/assets/chunk-QZHKN3VN-JLEIDbAK.js +1 -0
- package/dist/dashboard/assets/chunk-TZMSLE5B-BB4GoxaV.js +1 -0
- package/dist/dashboard/assets/classDiagram-2ON5EDUG-CMpOlMXG.js +1 -0
- package/dist/dashboard/assets/classDiagram-v2-WZHVMYZB-CMpOlMXG.js +1 -0
- package/dist/dashboard/assets/clone-CHi7kZ5h.js +1 -0
- package/dist/dashboard/assets/cose-bilkent-S5V4N54A-DLEOmN1S.js +1 -0
- package/dist/dashboard/assets/cytoscape.esm-5J0xJHOV.js +321 -0
- package/dist/dashboard/assets/dagre-6UL2VRFP-BKeNwVe7.js +4 -0
- package/dist/dashboard/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/dashboard/assets/diagram-PSM6KHXK-BwTCZWHJ.js +24 -0
- package/dist/dashboard/assets/diagram-QEK2KX5R-Bg109hRA.js +43 -0
- package/dist/dashboard/assets/diagram-S2PKOQOG-CjzZEN97.js +24 -0
- package/dist/dashboard/assets/erDiagram-Q2GNP2WA-C_o1kY7B.js +60 -0
- package/dist/dashboard/assets/flowDiagram-NV44I4VS-CGcmMJTD.js +162 -0
- package/dist/dashboard/assets/ganttDiagram-JELNMOA3-Dh1Atgfm.js +267 -0
- package/dist/dashboard/assets/gitGraphDiagram-NY62KEGX-BkHRiPWO.js +65 -0
- package/dist/dashboard/assets/graph-gTAnu0gr.js +1 -0
- package/dist/dashboard/assets/index-DFP3ko3G.css +1 -0
- package/dist/dashboard/assets/index-Dr2pvJml.js +189 -0
- package/dist/dashboard/assets/infoDiagram-WHAUD3N6-CdqkT51i.js +2 -0
- package/dist/dashboard/assets/init-Gi6I4Gst.js +1 -0
- package/dist/dashboard/assets/journeyDiagram-XKPGCS4Q-D6H-T7IF.js +139 -0
- package/dist/dashboard/assets/kanban-definition-3W4ZIXB7-CdtlB9dE.js +89 -0
- package/dist/dashboard/assets/katex-DhXJpUyf.js +261 -0
- package/dist/dashboard/assets/layout-5P3G0oCF.js +1 -0
- package/dist/dashboard/assets/linear-BEr0iAwY.js +1 -0
- package/dist/dashboard/assets/mermaid.core-DY8Lk9ir.js +250 -0
- package/dist/dashboard/assets/min-rFBMICyx.js +1 -0
- package/dist/dashboard/assets/mindmap-definition-VGOIOE7T-BBgtd54T.js +68 -0
- package/dist/dashboard/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/dashboard/assets/pieDiagram-ADFJNKIX-fgAzUnCv.js +30 -0
- package/dist/dashboard/assets/quadrantDiagram-AYHSOK5B-BtBFRMty.js +7 -0
- package/dist/dashboard/assets/requirementDiagram-UZGBJVZJ-Dsb2J8qM.js +64 -0
- package/dist/dashboard/assets/sankeyDiagram-TZEHDZUN-DgeXoGZy.js +10 -0
- package/dist/dashboard/assets/sequenceDiagram-WL72ISMW-CW8ApGYu.js +145 -0
- package/dist/dashboard/assets/stateDiagram-FKZM4ZOC-CFKNRDGr.js +1 -0
- package/dist/dashboard/assets/stateDiagram-v2-4FDKWEC3-B3pJgIRu.js +1 -0
- package/dist/dashboard/assets/timeline-definition-IT6M3QCI-DeE7K_Tn.js +61 -0
- package/dist/dashboard/assets/treemap-KMMF4GRG-DgksShPO.js +128 -0
- package/dist/dashboard/assets/xychartDiagram-PRI3JC2R-C8fn_Zm6.js +7 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/routes/marketplace.d.ts.map +1 -1
- package/dist/routes/marketplace.js +179 -1
- package/dist/routes/marketplace.js.map +1 -1
- package/dist/routes/remove.d.ts.map +1 -1
- package/dist/routes/remove.js +4 -3
- package/dist/routes/remove.js.map +1 -1
- package/dist/routes/state.d.ts.map +1 -1
- package/dist/routes/state.js +63 -11
- package/dist/routes/state.js.map +1 -1
- package/package.json +2 -2
- package/dist/dashboard/assets/index-B15EvyT1.css +0 -1
- package/dist/dashboard/assets/index-Bt5n5lhF.js +0 -150
|
@@ -2,51 +2,9 @@ import { MARKETPLACE_SOURCES as MS } from "@mycelish/core";
|
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export async function fetchNpmDownloads(names) {
|
|
9
|
-
const result = {};
|
|
10
|
-
if (names.length === 0)
|
|
11
|
-
return result;
|
|
12
|
-
const fetches = names.slice(0, 20).map(async (name) => {
|
|
13
|
-
try {
|
|
14
|
-
const res = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(name)}`);
|
|
15
|
-
if (res.ok) {
|
|
16
|
-
const data = (await res.json());
|
|
17
|
-
if (data.downloads)
|
|
18
|
-
result[name] = data.downloads;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// Non-critical
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
await Promise.allSettled(fetches);
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
|
-
// ============================================================================
|
|
29
|
-
// OpenSkills (npm registry)
|
|
30
|
-
// ============================================================================
|
|
31
|
-
export async function searchOpenSkills(query) {
|
|
32
|
-
const res = await fetch(`https://registry.npmjs.org/-/v1/search?text=openskills+${encodeURIComponent(query)}&size=12`);
|
|
33
|
-
if (!res.ok)
|
|
34
|
-
throw new Error(`openskills search failed: ${res.statusText}`);
|
|
35
|
-
const data = (await res.json());
|
|
36
|
-
const names = data.objects.map(o => o.package.name);
|
|
37
|
-
const downloads = await fetchNpmDownloads(names);
|
|
38
|
-
const entries = data.objects.map((o) => ({
|
|
39
|
-
name: o.package.name,
|
|
40
|
-
description: o.package.description || "",
|
|
41
|
-
author: o.package.author?.name,
|
|
42
|
-
version: o.package.version,
|
|
43
|
-
latestVersion: o.package.version,
|
|
44
|
-
downloads: downloads[o.package.name],
|
|
45
|
-
source: MS.OPENSKILLS,
|
|
46
|
-
type: "skill",
|
|
47
|
-
}));
|
|
48
|
-
return { entries, total: entries.length, source: MS.OPENSKILLS };
|
|
49
|
-
}
|
|
5
|
+
import { cachedFetch } from "./marketplace-cache.js";
|
|
6
|
+
import { computeContentHash } from "./content-hash.js";
|
|
7
|
+
import { MARKETPLACE_FETCH_LIMIT, TIMEOUT_GITHUB, TIMEOUT_UNGH, TIMEOUT_NPM, TIMEOUT_GLAMA, BATCH_NPM, BATCH_GITHUB, } from "./marketplace-constants.js";
|
|
50
8
|
// ============================================================================
|
|
51
9
|
// Claude Plugins (local installed_plugins.json v2)
|
|
52
10
|
// ============================================================================
|
|
@@ -55,29 +13,19 @@ export async function listInstalledPlugins() {
|
|
|
55
13
|
const filePath = path.join(os.homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
56
14
|
const raw = await fs.readFile(filePath, "utf-8");
|
|
57
15
|
const data = JSON.parse(raw);
|
|
58
|
-
if (
|
|
16
|
+
if (data.version === 2 && data.plugins) {
|
|
59
17
|
return parseV2Plugins(data.plugins);
|
|
60
18
|
}
|
|
61
|
-
if (Array.isArray(data)) {
|
|
62
|
-
return data.map((p) => ({
|
|
63
|
-
name: p.name,
|
|
64
|
-
description: p.description || "",
|
|
65
|
-
version: p.version,
|
|
66
|
-
author: p.author,
|
|
67
|
-
installed: true,
|
|
68
|
-
source: MS.CLAUDE_PLUGINS,
|
|
69
|
-
type: "plugin",
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
19
|
return [];
|
|
73
20
|
}
|
|
74
21
|
catch (e) {
|
|
75
22
|
if (e && typeof e === "object" && e.code !== "ENOENT") {
|
|
76
|
-
|
|
23
|
+
// Non-critical warning — skip logging to avoid noise
|
|
77
24
|
}
|
|
78
25
|
return [];
|
|
79
26
|
}
|
|
80
27
|
}
|
|
28
|
+
/** Pure parser — no filtering. Visibility policy is handled by getLivePluginState. */
|
|
81
29
|
function parseV2Plugins(plugins) {
|
|
82
30
|
const entries = [];
|
|
83
31
|
for (const [key, installs] of Object.entries(plugins)) {
|
|
@@ -91,27 +39,102 @@ function parseV2Plugins(plugins) {
|
|
|
91
39
|
name: pluginName,
|
|
92
40
|
description: marketplace ? `From ${marketplace}` : "",
|
|
93
41
|
version: latest.version,
|
|
94
|
-
latestVersion: latest.version,
|
|
95
42
|
installedVersion: latest.version,
|
|
96
43
|
installed: true,
|
|
97
44
|
updatedAt: latest.lastUpdated ? new Date(latest.lastUpdated).toISOString().slice(0, 10) : undefined,
|
|
98
45
|
source: MS.CLAUDE_PLUGINS,
|
|
99
46
|
type: "plugin",
|
|
47
|
+
category: marketplace,
|
|
100
48
|
});
|
|
101
49
|
}
|
|
102
50
|
return entries;
|
|
103
51
|
}
|
|
104
52
|
export async function searchClaudePlugins(query) {
|
|
105
53
|
const plugins = await listInstalledPlugins();
|
|
54
|
+
await enrichPluginsWithLatestVersions(plugins);
|
|
106
55
|
const q = query.toLowerCase();
|
|
107
56
|
const entries = plugins.filter((p) => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q));
|
|
108
57
|
return { entries, total: entries.length, source: MS.CLAUDE_PLUGINS };
|
|
109
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Enrich installed plugins with latest versions from their marketplace repos.
|
|
61
|
+
* Reads known_marketplaces.json → fetches marketplace.json from GitHub → extracts latest versions.
|
|
62
|
+
*/
|
|
63
|
+
export async function enrichPluginsWithLatestVersions(plugins) {
|
|
64
|
+
try {
|
|
65
|
+
const knownPath = path.join(os.homedir(), ".claude", "plugins", "known_marketplaces.json");
|
|
66
|
+
const raw = await fs.readFile(knownPath, "utf-8");
|
|
67
|
+
const known = JSON.parse(raw);
|
|
68
|
+
// Build a map: marketplace name → GitHub owner/repo
|
|
69
|
+
const repoMap = new Map();
|
|
70
|
+
for (const [name, info] of Object.entries(known)) {
|
|
71
|
+
if (info.source?.source === "github" && info.source.repo) {
|
|
72
|
+
const parts = info.source.repo.split("/");
|
|
73
|
+
if (parts.length === 2)
|
|
74
|
+
repoMap.set(name, { owner: parts[0], repo: parts[1] });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (repoMap.size === 0)
|
|
78
|
+
return;
|
|
79
|
+
const marketplacePlugins = new Map();
|
|
80
|
+
const fetches = [...repoMap.entries()].map(async ([marketplace, { owner, repo }]) => {
|
|
81
|
+
try {
|
|
82
|
+
const data = await cachedFetch(`plugin-meta-${marketplace}`, async () => {
|
|
83
|
+
const headers = {};
|
|
84
|
+
const ghToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
85
|
+
if (ghToken)
|
|
86
|
+
headers.Authorization = `Bearer ${ghToken}`;
|
|
87
|
+
const res = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/main/.claude-plugin/marketplace.json`, { headers, signal: AbortSignal.timeout(TIMEOUT_GITHUB) });
|
|
88
|
+
if (!res.ok)
|
|
89
|
+
return { plugins: [] };
|
|
90
|
+
return (await res.json());
|
|
91
|
+
});
|
|
92
|
+
const meta = new Map();
|
|
93
|
+
for (const p of data.plugins ?? []) {
|
|
94
|
+
if (!p.name)
|
|
95
|
+
continue;
|
|
96
|
+
// Extract the actual plugin repo URL from the source field
|
|
97
|
+
let pluginUrl;
|
|
98
|
+
if (p.source?.url) {
|
|
99
|
+
pluginUrl = p.source.url.replace(/\.git$/, "");
|
|
100
|
+
}
|
|
101
|
+
meta.set(p.name, { version: p.version, url: pluginUrl });
|
|
102
|
+
}
|
|
103
|
+
marketplacePlugins.set(marketplace, meta);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Non-critical
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
await Promise.allSettled(fetches);
|
|
110
|
+
// Enrich entries with latest versions and correct plugin URLs
|
|
111
|
+
for (const plugin of plugins) {
|
|
112
|
+
const mpName = plugin.category;
|
|
113
|
+
const mpMeta = mpName ? marketplacePlugins.get(mpName) : undefined;
|
|
114
|
+
const meta = mpMeta?.get(plugin.name)
|
|
115
|
+
?? [...marketplacePlugins.values()].find(m => m.has(plugin.name))?.get(plugin.name);
|
|
116
|
+
if (meta?.version)
|
|
117
|
+
plugin.latestVersion = meta.version;
|
|
118
|
+
// Use the actual plugin repo URL, not the marketplace repo
|
|
119
|
+
if (!plugin.url && meta?.url) {
|
|
120
|
+
plugin.url = meta.url;
|
|
121
|
+
}
|
|
122
|
+
// Fallback: marketplace repo if no per-plugin URL
|
|
123
|
+
if (!plugin.url && mpName && repoMap.has(mpName)) {
|
|
124
|
+
const { owner, repo } = repoMap.get(mpName);
|
|
125
|
+
plugin.url = `https://github.com/${owner}/${repo}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// known_marketplaces.json missing or unreadable — skip
|
|
131
|
+
}
|
|
132
|
+
}
|
|
110
133
|
const MCP_REGISTRY_URL = "https://registry.modelcontextprotocol.io";
|
|
111
134
|
export async function fetchMcpServers(query) {
|
|
112
135
|
const url = query
|
|
113
|
-
? `${MCP_REGISTRY_URL}/v0.1/servers?
|
|
114
|
-
: `${MCP_REGISTRY_URL}/v0.1/servers?limit
|
|
136
|
+
? `${MCP_REGISTRY_URL}/v0.1/servers?search=${encodeURIComponent(query)}&limit=${MARKETPLACE_FETCH_LIMIT}`
|
|
137
|
+
: `${MCP_REGISTRY_URL}/v0.1/servers?limit=${MARKETPLACE_FETCH_LIMIT}`;
|
|
115
138
|
const res = await fetch(url);
|
|
116
139
|
if (!res.ok)
|
|
117
140
|
throw new Error(`MCP Registry failed: ${res.statusText}`);
|
|
@@ -120,6 +143,8 @@ export async function fetchMcpServers(query) {
|
|
|
120
143
|
}
|
|
121
144
|
export function mcpServerToEntry(s) {
|
|
122
145
|
const srv = s.server;
|
|
146
|
+
const url = srv.repository?.url || srv.websiteUrl || undefined;
|
|
147
|
+
const npmPkg = srv.packages?.find(p => p.registryType === "npm")?.identifier;
|
|
123
148
|
return {
|
|
124
149
|
name: srv.name,
|
|
125
150
|
description: srv.description || "",
|
|
@@ -127,23 +152,69 @@ export function mcpServerToEntry(s) {
|
|
|
127
152
|
latestVersion: srv.version,
|
|
128
153
|
source: MS.MCP_REGISTRY,
|
|
129
154
|
type: "mcp",
|
|
155
|
+
url,
|
|
156
|
+
npmPackage: npmPkg,
|
|
130
157
|
};
|
|
131
158
|
}
|
|
132
|
-
export async function searchMcpRegistry(query) {
|
|
133
|
-
const servers =
|
|
159
|
+
export async function searchMcpRegistry(query, options) {
|
|
160
|
+
const servers = query
|
|
161
|
+
? await fetchMcpServers(query) // user search: live
|
|
162
|
+
: await cachedFetch("mcp-registry", () => fetchMcpServers(""), options); // browse: cached
|
|
134
163
|
const entries = servers.map(mcpServerToEntry);
|
|
135
164
|
return { entries, total: entries.length, source: MS.MCP_REGISTRY };
|
|
136
165
|
}
|
|
166
|
+
const GLAMA_API = "https://glama.ai/api/mcp/v1/servers";
|
|
167
|
+
export async function fetchGlamaServers(query) {
|
|
168
|
+
const params = new URLSearchParams({ limit: String(MARKETPLACE_FETCH_LIMIT) });
|
|
169
|
+
if (query)
|
|
170
|
+
params.set("query", query);
|
|
171
|
+
const res = await fetch(`${GLAMA_API}?${params}`, { signal: AbortSignal.timeout(TIMEOUT_GLAMA) });
|
|
172
|
+
if (!res.ok)
|
|
173
|
+
throw new Error(`Glama API failed: ${res.statusText}`);
|
|
174
|
+
const data = (await res.json());
|
|
175
|
+
return data.servers || [];
|
|
176
|
+
}
|
|
177
|
+
export function glamaServerToEntry(s) {
|
|
178
|
+
return {
|
|
179
|
+
name: s.name,
|
|
180
|
+
description: s.description || "",
|
|
181
|
+
source: MS.GLAMA,
|
|
182
|
+
type: "mcp",
|
|
183
|
+
url: s.repository?.url || s.url || undefined,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export async function searchGlama(query, options) {
|
|
187
|
+
const servers = query
|
|
188
|
+
? await fetchGlamaServers(query)
|
|
189
|
+
: await cachedFetch("glama", () => fetchGlamaServers(""), options);
|
|
190
|
+
const entries = servers.map(glamaServerToEntry);
|
|
191
|
+
return { entries, total: entries.length, source: MS.GLAMA };
|
|
192
|
+
}
|
|
193
|
+
// ============================================================================
|
|
194
|
+
// GitHub tree (uses cachedFetch for L1+L2 caching)
|
|
195
|
+
// ============================================================================
|
|
196
|
+
async function fetchGitHubTree(owner, repo, options) {
|
|
197
|
+
return cachedFetch(`github-${owner}-${repo}`, async () => {
|
|
198
|
+
const headers = { Accept: "application/vnd.github.v3+json" };
|
|
199
|
+
const ghToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
200
|
+
if (ghToken)
|
|
201
|
+
headers.Authorization = `Bearer ${ghToken}`;
|
|
202
|
+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees/main?recursive=1`, { headers });
|
|
203
|
+
if (!res.ok) {
|
|
204
|
+
const hint = res.status === 403 ? " (rate limited — try again later or set GITHUB_TOKEN)" : "";
|
|
205
|
+
throw new Error(`GitHub API ${res.status} for ${owner}/${repo}${hint}`);
|
|
206
|
+
}
|
|
207
|
+
const body = (await res.json());
|
|
208
|
+
return body.tree;
|
|
209
|
+
}, options);
|
|
210
|
+
}
|
|
137
211
|
// ============================================================================
|
|
138
212
|
// Anthropic Skills (GitHub)
|
|
139
213
|
// ============================================================================
|
|
140
|
-
export async function fetchAnthropicSkillsList() {
|
|
141
|
-
const
|
|
142
|
-
if (!res.ok)
|
|
143
|
-
return [];
|
|
144
|
-
const data = (await res.json());
|
|
214
|
+
export async function fetchAnthropicSkillsList(options) {
|
|
215
|
+
const tree = await fetchGitHubTree("anthropics", "skills", options);
|
|
145
216
|
const skills = [];
|
|
146
|
-
for (const t of
|
|
217
|
+
for (const t of tree) {
|
|
147
218
|
if (t.type === "blob" && t.path.endsWith("/SKILL.md") && t.path.startsWith("skills/")) {
|
|
148
219
|
const parts = t.path.split("/");
|
|
149
220
|
if (parts.length === 3)
|
|
@@ -152,8 +223,11 @@ export async function fetchAnthropicSkillsList() {
|
|
|
152
223
|
}
|
|
153
224
|
return skills;
|
|
154
225
|
}
|
|
155
|
-
export async function searchAnthropicSkills(query) {
|
|
156
|
-
const allSkills = await
|
|
226
|
+
export async function searchAnthropicSkills(query, options) {
|
|
227
|
+
const [allSkills, stars] = await Promise.all([
|
|
228
|
+
fetchAnthropicSkillsList(options),
|
|
229
|
+
fetchGitHubRepoStars("anthropics", "skills", options),
|
|
230
|
+
]);
|
|
157
231
|
const q = query.toLowerCase();
|
|
158
232
|
const filtered = q ? allSkills.filter(s => s.toLowerCase().includes(q)) : allSkills;
|
|
159
233
|
const entries = filtered.map(name => ({
|
|
@@ -162,40 +236,290 @@ export async function searchAnthropicSkills(query) {
|
|
|
162
236
|
author: "anthropics",
|
|
163
237
|
source: MS.ANTHROPIC_SKILLS,
|
|
164
238
|
type: "skill",
|
|
239
|
+
stars,
|
|
240
|
+
url: `https://github.com/anthropics/skills/tree/main/skills/${name}`,
|
|
165
241
|
}));
|
|
166
242
|
return { entries, total: entries.length, source: MS.ANTHROPIC_SKILLS };
|
|
167
243
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Parse a github.com URL into owner/repo.
|
|
246
|
+
* Returns null if not a GitHub URL.
|
|
247
|
+
*/
|
|
248
|
+
const GITHUB_NAME_RE = /^[a-zA-Z0-9._-]+$/;
|
|
249
|
+
export function parseGitHubUrl(url) {
|
|
250
|
+
const m = url.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
251
|
+
if (!m)
|
|
252
|
+
return null;
|
|
253
|
+
const owner = m[1];
|
|
254
|
+
const repo = m[2].replace(/\.git$/, "");
|
|
255
|
+
if (!GITHUB_NAME_RE.test(owner) || !GITHUB_NAME_RE.test(repo))
|
|
256
|
+
return null;
|
|
257
|
+
return { owner, repo };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Search a GitHub repo for skills, agents, and commands.
|
|
261
|
+
* Uses the recursive tree endpoint (same pattern as fetchAnthropicSkillsList).
|
|
262
|
+
*/
|
|
263
|
+
async function fetchGitHubRepoStars(owner, repo, options) {
|
|
264
|
+
try {
|
|
265
|
+
const data = await cachedFetch(`github-stars-${owner}-${repo}`, async () => {
|
|
266
|
+
// Tier 1: ungh.cc — free, no auth, no rate limit
|
|
267
|
+
try {
|
|
268
|
+
const unghRes = await fetch(`https://ungh.cc/repos/${owner}/${repo}`, { signal: AbortSignal.timeout(TIMEOUT_UNGH) });
|
|
269
|
+
if (unghRes.ok) {
|
|
270
|
+
const json = (await unghRes.json());
|
|
271
|
+
if (json.repo?.stars != null)
|
|
272
|
+
return { stars: json.repo.stars };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// Fall through to GitHub API
|
|
277
|
+
}
|
|
278
|
+
// Tier 2/3: GitHub API (with token if available, unauth otherwise)
|
|
279
|
+
const headers = { Accept: "application/vnd.github.v3+json" };
|
|
280
|
+
const ghToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
281
|
+
if (ghToken)
|
|
282
|
+
headers.Authorization = `Bearer ${ghToken}`;
|
|
283
|
+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers, signal: AbortSignal.timeout(TIMEOUT_GITHUB) });
|
|
284
|
+
if (!res.ok)
|
|
285
|
+
throw new Error(`GitHub API ${res.status} for ${owner}/${repo}`);
|
|
286
|
+
const json = (await res.json());
|
|
287
|
+
return { stars: json.stargazers_count };
|
|
288
|
+
}, options);
|
|
289
|
+
return data.stars;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
export async function searchGitHubRepo(owner, repo, query, sourceName, options) {
|
|
296
|
+
const [items, stars] = await Promise.all([
|
|
297
|
+
fetchGitHubRepoItems(owner, repo, options),
|
|
298
|
+
fetchGitHubRepoStars(owner, repo, options),
|
|
299
|
+
]);
|
|
300
|
+
const q = query.toLowerCase();
|
|
301
|
+
const filtered = q
|
|
302
|
+
? items.filter(i => i.name.toLowerCase().includes(q) || (i.description?.toLowerCase().includes(q) ?? false))
|
|
303
|
+
: items;
|
|
304
|
+
const entries = filtered.map(item => ({
|
|
305
|
+
name: item.name,
|
|
306
|
+
description: item.description || `${item.type} from ${owner}/${repo}`,
|
|
307
|
+
author: owner,
|
|
308
|
+
source: sourceName,
|
|
309
|
+
type: item.type,
|
|
310
|
+
stars,
|
|
311
|
+
url: `https://github.com/${owner}/${repo}/tree/main/${item.path}`,
|
|
181
312
|
}));
|
|
182
|
-
|
|
313
|
+
// If the repo has multiple item types, add a plugin entry representing the whole repo
|
|
314
|
+
const types = new Set(items.map(i => i.type));
|
|
315
|
+
if (types.size > 1 && (!q || repo.toLowerCase().includes(q) || owner.toLowerCase().includes(q))) {
|
|
316
|
+
const counts = [...types].map(t => `${items.filter(i => i.type === t).length} ${t}s`).join(", ");
|
|
317
|
+
entries.unshift({
|
|
318
|
+
name: repo,
|
|
319
|
+
description: `Plugin bundle: ${counts}`,
|
|
320
|
+
author: owner,
|
|
321
|
+
source: sourceName,
|
|
322
|
+
type: "plugin",
|
|
323
|
+
stars,
|
|
324
|
+
url: `https://github.com/${owner}/${repo}`,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
return { entries, total: entries.length, source: sourceName };
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Fetch all items (skills, agents, commands) from a GitHub repo tree.
|
|
331
|
+
*/
|
|
332
|
+
export async function fetchGitHubRepoItems(owner, repo, options) {
|
|
333
|
+
const tree = await fetchGitHubTree(owner, repo, options);
|
|
334
|
+
const items = [];
|
|
335
|
+
const dirMap = {
|
|
336
|
+
skills: "skill",
|
|
337
|
+
agents: "agent",
|
|
338
|
+
commands: "command",
|
|
339
|
+
};
|
|
340
|
+
for (const t of tree) {
|
|
341
|
+
if (t.type !== "blob")
|
|
342
|
+
continue;
|
|
343
|
+
for (const [dir, itemType] of Object.entries(dirMap)) {
|
|
344
|
+
if (t.path.startsWith(`${dir}/`) && t.path.endsWith(".md")) {
|
|
345
|
+
const parts = t.path.split("/");
|
|
346
|
+
// skills/name/SKILL.md (depth 3) or agents/name.md (depth 2)
|
|
347
|
+
let name;
|
|
348
|
+
if (itemType === "skill" && parts.length === 3 && parts[2] === "SKILL.md") {
|
|
349
|
+
name = parts[1];
|
|
350
|
+
}
|
|
351
|
+
else if (itemType !== "skill" && parts.length === 2) {
|
|
352
|
+
name = parts[1].replace(/\.md$/, "");
|
|
353
|
+
}
|
|
354
|
+
if (name) {
|
|
355
|
+
items.push({ name, type: itemType, path: t.path });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return items;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Install an item from a GitHub repo by downloading its raw content.
|
|
364
|
+
*/
|
|
365
|
+
export async function installGitHubRepoItem(owner, repo, entry) {
|
|
366
|
+
const itemType = entry.type;
|
|
367
|
+
let remotePath;
|
|
368
|
+
let localDir;
|
|
369
|
+
let fileName;
|
|
370
|
+
const globalDir = path.join(os.homedir(), ".mycelium", "global");
|
|
371
|
+
if (itemType === "skill") {
|
|
372
|
+
remotePath = `skills/${encodeURIComponent(entry.name)}/SKILL.md`;
|
|
373
|
+
localDir = path.join(globalDir, "skills", entry.name);
|
|
374
|
+
fileName = "SKILL.md";
|
|
375
|
+
}
|
|
376
|
+
else if (itemType === "agent") {
|
|
377
|
+
remotePath = `agents/${encodeURIComponent(entry.name)}.md`;
|
|
378
|
+
localDir = path.join(globalDir, "agents");
|
|
379
|
+
fileName = `${entry.name}.md`;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
remotePath = `commands/${encodeURIComponent(entry.name)}.md`;
|
|
383
|
+
localDir = path.join(globalDir, "commands");
|
|
384
|
+
fileName = `${entry.name}.md`;
|
|
385
|
+
}
|
|
386
|
+
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/main/${remotePath}`;
|
|
387
|
+
const ghRes = await fetch(rawUrl);
|
|
388
|
+
if (!ghRes.ok)
|
|
389
|
+
throw new Error(`Download failed: ${ghRes.statusText}`);
|
|
390
|
+
const content = await ghRes.text();
|
|
391
|
+
await fs.mkdir(localDir, { recursive: true });
|
|
392
|
+
const filePath = path.join(localDir, fileName);
|
|
393
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
394
|
+
return { success: true, path: filePath, contentHash: computeContentHash(content) };
|
|
183
395
|
}
|
|
184
396
|
// ============================================================================
|
|
185
|
-
//
|
|
397
|
+
// npm Download Enrichment
|
|
186
398
|
// ============================================================================
|
|
187
|
-
|
|
188
|
-
|
|
399
|
+
/**
|
|
400
|
+
* Enrich marketplace entries with npm weekly download counts.
|
|
401
|
+
* Tries the entry name as an npm package name (works for most MCP servers).
|
|
402
|
+
* Uses cachedFetch to avoid redundant API calls.
|
|
403
|
+
*/
|
|
404
|
+
export async function enrichWithNpmDownloads(entries) {
|
|
405
|
+
// Only enrich MCP-type entries (not plugins — plugin names don't match npm packages)
|
|
406
|
+
const candidates = entries.filter(e => e.downloads == null && e.type === "mcp");
|
|
407
|
+
if (candidates.length === 0)
|
|
408
|
+
return;
|
|
409
|
+
// Batch: max BATCH_NPM concurrent
|
|
410
|
+
const batch = candidates.slice(0, BATCH_NPM);
|
|
411
|
+
await Promise.allSettled(batch.map(async (entry) => {
|
|
412
|
+
try {
|
|
413
|
+
// Use the npm package name — for scoped names like @modelcontextprotocol/server-*
|
|
414
|
+
// the entry name is usually the npm package name for MCP entries
|
|
415
|
+
const pkgName = entry.name;
|
|
416
|
+
const data = await cachedFetch(`npm-dl-${pkgName}`, async () => {
|
|
417
|
+
// First verify the package exists and is MCP-related (check keywords)
|
|
418
|
+
const metaRes = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkgName)}`, { signal: AbortSignal.timeout(TIMEOUT_NPM), headers: { Accept: "application/vnd.npm.install-v1+json" } });
|
|
419
|
+
if (!metaRes.ok)
|
|
420
|
+
return { downloads: undefined };
|
|
421
|
+
const meta = (await metaRes.json());
|
|
422
|
+
// Validate it's actually an MCP package (description or keywords mention "mcp")
|
|
423
|
+
const desc = (meta.description || "").toLowerCase();
|
|
424
|
+
const kw = (meta.keywords || []).join(" ").toLowerCase();
|
|
425
|
+
if (!desc.includes("mcp") && !kw.includes("mcp") && !desc.includes("model context protocol")) {
|
|
426
|
+
return { downloads: undefined };
|
|
427
|
+
}
|
|
428
|
+
const dlRes = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(pkgName)}`, { signal: AbortSignal.timeout(TIMEOUT_NPM) });
|
|
429
|
+
if (!dlRes.ok)
|
|
430
|
+
return { downloads: undefined };
|
|
431
|
+
const json = (await dlRes.json());
|
|
432
|
+
return { downloads: json.downloads };
|
|
433
|
+
});
|
|
434
|
+
if (data.downloads != null && data.downloads > 0) {
|
|
435
|
+
entry.downloads = data.downloads;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
// Non-critical
|
|
440
|
+
}
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
// ============================================================================
|
|
444
|
+
// GitHub Stars Enrichment (for entries with GitHub URLs)
|
|
445
|
+
// ============================================================================
|
|
446
|
+
/**
|
|
447
|
+
* Resolve a GitHub URL from an npm package name via the npm registry.
|
|
448
|
+
* Returns a github.com URL or undefined.
|
|
449
|
+
*/
|
|
450
|
+
async function resolveGitHubUrlFromNpm(pkg) {
|
|
451
|
+
try {
|
|
452
|
+
const data = await cachedFetch(`npm-repo-${pkg}`, async () => {
|
|
453
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkg)}`, { signal: AbortSignal.timeout(TIMEOUT_NPM) });
|
|
454
|
+
if (!res.ok)
|
|
455
|
+
throw new Error(`npm ${res.status}`);
|
|
456
|
+
const json = (await res.json());
|
|
457
|
+
const repoUrl = typeof json.repository === "string" ? json.repository : json.repository?.url;
|
|
458
|
+
return { repoUrl: repoUrl || null };
|
|
459
|
+
});
|
|
460
|
+
if (data.repoUrl && data.repoUrl.includes("github.com")) {
|
|
461
|
+
// Normalize git+https://github.com/foo/bar.git → https://github.com/foo/bar
|
|
462
|
+
return data.repoUrl.replace(/^git\+/, "").replace(/\.git$/, "");
|
|
463
|
+
}
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Enrich marketplace entries with GitHub star counts.
|
|
472
|
+
* For entries without a GitHub URL but with an npm package, resolves the repo via npm first.
|
|
473
|
+
* Uses ungh.cc → GitHub API (token) → GitHub API (unauth) priority chain.
|
|
474
|
+
*/
|
|
475
|
+
export async function enrichWithGitHubStars(entries) {
|
|
476
|
+
// Phase 1: resolve GitHub URLs from npm packages for entries missing a GitHub URL
|
|
477
|
+
const needsNpmResolve = entries.filter(e => e.stars == null && e.npmPackage && (!e.url || !e.url.includes("github.com")));
|
|
478
|
+
if (needsNpmResolve.length > 0) {
|
|
479
|
+
await Promise.allSettled(needsNpmResolve.map(async (entry) => {
|
|
480
|
+
const ghUrl = await resolveGitHubUrlFromNpm(entry.npmPackage);
|
|
481
|
+
if (ghUrl)
|
|
482
|
+
entry.url = ghUrl;
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
// Phase 2: enrich with stars
|
|
486
|
+
const needsStars = entries.filter(e => e.url && e.stars == null && e.url.includes("github.com"));
|
|
487
|
+
if (needsStars.length === 0)
|
|
488
|
+
return;
|
|
489
|
+
// Extract owner/repo from URL, dedupe repos
|
|
490
|
+
const repoMap = new Map();
|
|
491
|
+
for (const entry of needsStars) {
|
|
492
|
+
const parsed = parseGitHubUrl(entry.url);
|
|
493
|
+
if (!parsed)
|
|
494
|
+
continue;
|
|
495
|
+
const key = `${parsed.owner}/${parsed.repo}`;
|
|
496
|
+
if (!repoMap.has(key))
|
|
497
|
+
repoMap.set(key, []);
|
|
498
|
+
repoMap.get(key).push(entry);
|
|
499
|
+
}
|
|
500
|
+
// Fetch stars in parallel (max BATCH_GITHUB repos per batch to be polite)
|
|
501
|
+
const repos = [...repoMap.entries()].slice(0, BATCH_GITHUB);
|
|
502
|
+
await Promise.allSettled(repos.map(async ([repo, repoEntries]) => {
|
|
503
|
+
try {
|
|
504
|
+
const [owner, repoName] = repo.split("/");
|
|
505
|
+
const stars = await fetchGitHubRepoStars(owner, repoName);
|
|
506
|
+
if (stars != null) {
|
|
507
|
+
for (const entry of repoEntries)
|
|
508
|
+
entry.stars = stars;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
// Non-critical — skip enrichment for failed repos
|
|
513
|
+
}
|
|
514
|
+
}));
|
|
189
515
|
}
|
|
190
516
|
// ============================================================================
|
|
191
517
|
// Searcher map
|
|
192
518
|
// ============================================================================
|
|
193
519
|
export const KNOWN_SEARCHERS = {
|
|
194
|
-
[MS.SKILLSMP]: searchSkillsmp,
|
|
195
|
-
[MS.OPENSKILLS]: searchOpenSkills,
|
|
196
520
|
[MS.CLAUDE_PLUGINS]: searchClaudePlugins,
|
|
197
521
|
[MS.MCP_REGISTRY]: searchMcpRegistry,
|
|
522
|
+
[MS.GLAMA]: searchGlama,
|
|
198
523
|
[MS.ANTHROPIC_SKILLS]: searchAnthropicSkills,
|
|
199
|
-
[MS.CLAWHUB]: searchClawHub,
|
|
200
524
|
};
|
|
201
525
|
//# sourceMappingURL=marketplace-sources.js.map
|