@shnitzel/plugscout 0.3.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.
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/assets/cli/logo.txt +24 -0
- package/config/item-insights.json +316 -0
- package/config/providers.json +46 -0
- package/config/ranking-policy.json +14 -0
- package/config/recommendation-weights.json +7 -0
- package/config/registries.json +1423 -0
- package/config/security-policy.json +19 -0
- package/config/sources.json +30 -0
- package/data/catalog/items.json +182109 -0
- package/data/catalog/mcps.json +163843 -0
- package/data/catalog/skills.json +4768 -0
- package/data/catalog/sync-state.json +62 -0
- package/data/curated/mcps.json +78 -0
- package/data/curated/skills.json +174 -0
- package/data/quarantine/quarantined.json +3 -0
- package/data/raw/2024-05-15/mcps.json +20 -0
- package/data/raw/2024-05-20/skills.json +20 -0
- package/data/raw/2024-06-05/mcps.json +20 -0
- package/data/raw/2024-06-05/skills.json +29 -0
- package/data/security-reports/.gitkeep +0 -0
- package/data/security-reports/2026-02-06/report.json +8 -0
- package/data/security-reports/2026-02-10/report.json +9 -0
- package/data/security-reports/2026-02-11/report.json +9 -0
- package/data/security-reports/2026-02-12/report.json +9 -0
- package/data/security-reports/2026-02-13/report.json +8 -0
- package/data/security-reports/2026-02-14/report.json +8 -0
- package/data/security-reports/2026-02-23/report.json +8 -0
- package/data/security-reports/2026-02-25/report.json +8 -0
- package/data/security-reports/2026-02-26/report.json +8 -0
- package/data/security-reports/2026-03-10/report.json +8 -0
- package/data/security-reports/audits/.gitkeep +0 -0
- package/data/security-reports/audits/2026-02-06T10-17-33-872Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-06T10-17-33-881Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-22-24-474Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-22-24-483Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-42-12-305Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-42-12-319Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-43-15-728Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T20-43-15-738Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T21-22-14-047Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T21-22-14-051Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T21-29-59-237Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-10T21-29-59-243Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-21-51-074Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-21-51-123Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-28-33-021Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-28-33-026Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-34-43-623Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T20-34-43-625Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T21-06-33-281Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T21-06-33-285Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T21-08-58-836Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-11T21-08-58-843Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T12-26-07-150Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T12-26-07-159Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T14-37-36-565Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T14-37-36-569Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T14-47-32-103Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T14-47-32-213Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T14-47-47-769Z-mcp_filesystem.json +8 -0
- package/data/security-reports/audits/2026-02-12T15-05-49-085Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T15-05-49-087Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T16-37-42-204Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T16-37-42-243Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T16-47-16-589Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T16-47-16-596Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T17-38-24-899Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T17-38-24-905Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T17-56-00-835Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T17-56-00-840Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T18-19-26-005Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T18-19-26-008Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T18-34-38-642Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-12T18-34-38-645Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T05-44-27-648Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T05-44-27-656Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T05-48-50-827Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T05-48-50-900Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T10-53-33-850Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-13T10-53-33-853Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-14T17-51-27-279Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-14T17-51-27-282Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-14T19-43-39-991Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-14T19-43-39-997Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-23T19-24-43-515Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-23T19-24-43-518Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-45-02-763Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-45-02-778Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-46-58-957Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-46-58-960Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-57-37-133Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T14-57-37-139Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-03-23-507Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-03-23-513Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-03-41-157Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-03-41-162Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-05-18-042Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-05-18-048Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-39-08-519Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T15-39-08-526Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T18-35-54-463Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-25T18-35-54-466Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-21-092Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-21-093Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-27-076Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-27-079Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-27-084Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-27-086Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-37-249Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-37-258Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-37-259Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-52-37-274Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-28-389Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-28-391Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-33-868Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-33-880Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-33-892Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-33-900Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-43-064Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-43-066Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T05-53-43-068Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T14-55-47-466Z-claude-plugin_workspace-ops.json +8 -0
- package/data/security-reports/audits/2026-02-26T14-55-47-468Z-copilot-extension_repo-security.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-55-59-431Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-55-59-432Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-55-59-435Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-55-59-439Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-08-566Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-08-570Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-08-589Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-08-591Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-47-356Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-47-358Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-53-607Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-53-612Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-53-624Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-56-53-628Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-57-09-879Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-57-09-881Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-57-10-846Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-02-26T16-57-10-848Z-mcp_remote-browser.json +8 -0
- package/data/security-reports/audits/2026-03-10T18-15-05-007Z-claude-plugin_playwright.json +8 -0
- package/data/security-reports/audits/2026-03-10T18-36-16-092Z-claude-plugin_playwright.json +8 -0
- package/data/whitelist/approved.json +5 -0
- package/dist/catalog/adapter.js +39 -0
- package/dist/catalog/adapters/claude-code-marketplace-v1.js +260 -0
- package/dist/catalog/adapters/claude-connectors-scrape-v1.js +107 -0
- package/dist/catalog/adapters/claude-plugins-scrape-v1.js +107 -0
- package/dist/catalog/adapters/claude-plugins-v0.1.js +48 -0
- package/dist/catalog/adapters/copilot-extensions-v0.1.js +48 -0
- package/dist/catalog/adapters/copilot-plugin-marketplace-v1.js +117 -0
- package/dist/catalog/adapters/mcp-registry-v0.1.js +211 -0
- package/dist/catalog/adapters/openai-skills-github-v1.js +100 -0
- package/dist/catalog/adapters/openai-skills-v1.js +48 -0
- package/dist/catalog/adapters/shared.js +94 -0
- package/dist/catalog/remote-registry.js +196 -0
- package/dist/catalog/repository.js +161 -0
- package/dist/catalog/sync-state.js +61 -0
- package/dist/catalog/sync.js +153 -0
- package/dist/cli.js +25 -0
- package/dist/commands/ExplainerVideo.js +225 -0
- package/dist/commands/ingest.js +11 -0
- package/dist/commands/validate-data.js +10 -0
- package/dist/config/runtime.js +51 -0
- package/dist/config/sources.js +21 -0
- package/dist/ingestion/mcps.js +77 -0
- package/dist/ingestion/skills.js +76 -0
- package/dist/install/dependencies.js +58 -0
- package/dist/install/review-state.js +70 -0
- package/dist/install/skillsh.js +245 -0
- package/dist/interfaces/cli/doctor.js +90 -0
- package/dist/interfaces/cli/formatters/colors.js +24 -0
- package/dist/interfaces/cli/formatters/csv.js +10 -0
- package/dist/interfaces/cli/formatters/json.js +3 -0
- package/dist/interfaces/cli/formatters/markdown.js +6 -0
- package/dist/interfaces/cli/formatters/table.js +82 -0
- package/dist/interfaces/cli/index.js +1277 -0
- package/dist/interfaces/cli/options.js +93 -0
- package/dist/interfaces/cli/output.js +9 -0
- package/dist/interfaces/cli/types.js +1 -0
- package/dist/interfaces/cli/ui/home.js +114 -0
- package/dist/interfaces/cli/ui/web-report.js +384 -0
- package/dist/interfaces/cli/update-check.js +180 -0
- package/dist/lib/json.js +11 -0
- package/dist/lib/logger.js +13 -0
- package/dist/lib/paths.js +18 -0
- package/dist/lib/validation/contracts.js +245 -0
- package/dist/mcps/normalize.js +38 -0
- package/dist/models/records.js +31 -0
- package/dist/recommendation/engine.js +135 -0
- package/dist/recommendation/project-analysis.js +231 -0
- package/dist/recommendation/requirements.js +58 -0
- package/dist/security/assessment.js +56 -0
- package/dist/security/whitelist.js +70 -0
- package/dist/skills/normalize.js +39 -0
- package/dist/validation/curated.js +72 -0
- package/dist/video/Root.js +6 -0
- package/dist/video/index.js +3 -0
- package/package.json +102 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { adaptClaudeConnectorsScrapeEntries } from './adapters/claude-connectors-scrape-v1.js';
|
|
2
|
+
import { adaptClaudeCodeMarketplaceEntries } from './adapters/claude-code-marketplace-v1.js';
|
|
3
|
+
import { adaptClaudePluginsEntries } from './adapters/claude-plugins-v0.1.js';
|
|
4
|
+
import { adaptClaudePluginsScrapeEntries } from './adapters/claude-plugins-scrape-v1.js';
|
|
5
|
+
import { adaptCopilotPluginMarketplaceEntries } from './adapters/copilot-plugin-marketplace-v1.js';
|
|
6
|
+
import { adaptCopilotExtensionsEntries } from './adapters/copilot-extensions-v0.1.js';
|
|
7
|
+
import { adaptMcpRegistryEntries } from './adapters/mcp-registry-v0.1.js';
|
|
8
|
+
import { adaptOpenAiSkillsGitHubEntries } from './adapters/openai-skills-github-v1.js';
|
|
9
|
+
import { adaptOpenAiSkillsEntries } from './adapters/openai-skills-v1.js';
|
|
10
|
+
export function adaptRegistryEntries(registry, entries) {
|
|
11
|
+
if (registry.adapter === 'mcp-registry-v0.1') {
|
|
12
|
+
return adaptMcpRegistryEntries(registry.id, entries);
|
|
13
|
+
}
|
|
14
|
+
if (registry.adapter === 'openai-skills-v1') {
|
|
15
|
+
return adaptOpenAiSkillsEntries(registry.id, entries);
|
|
16
|
+
}
|
|
17
|
+
if (registry.adapter === 'openai-skills-github-v1') {
|
|
18
|
+
return adaptOpenAiSkillsGitHubEntries(registry.id, entries);
|
|
19
|
+
}
|
|
20
|
+
if (registry.adapter === 'claude-plugins-v0.1') {
|
|
21
|
+
return adaptClaudePluginsEntries(registry.id, entries);
|
|
22
|
+
}
|
|
23
|
+
if (registry.adapter === 'claude-plugins-scrape-v1') {
|
|
24
|
+
return adaptClaudePluginsScrapeEntries(registry.id, entries);
|
|
25
|
+
}
|
|
26
|
+
if (registry.adapter === 'claude-code-marketplace-v1') {
|
|
27
|
+
return adaptClaudeCodeMarketplaceEntries(registry, entries);
|
|
28
|
+
}
|
|
29
|
+
if (registry.adapter === 'copilot-extensions-v0.1') {
|
|
30
|
+
return adaptCopilotExtensionsEntries(registry.id, entries);
|
|
31
|
+
}
|
|
32
|
+
if (registry.adapter === 'copilot-plugin-marketplace-v1') {
|
|
33
|
+
return adaptCopilotPluginMarketplaceEntries(registry.id, entries);
|
|
34
|
+
}
|
|
35
|
+
if (registry.adapter === 'claude-connectors-scrape-v1') {
|
|
36
|
+
return adaptClaudeConnectorsScrapeEntries(registry.id, entries);
|
|
37
|
+
}
|
|
38
|
+
return entries;
|
|
39
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { dedupe, readString, sanitizeUrl, slugify, stripHtml, toCount, toScore } from './shared.js';
|
|
2
|
+
const TRUSTED_HOSTS = ['github.com', 'raw.githubusercontent.com'];
|
|
3
|
+
export function adaptClaudeCodeMarketplaceEntries(registry, entries) {
|
|
4
|
+
const seen = new Set();
|
|
5
|
+
const mapped = [];
|
|
6
|
+
for (const entry of entries) {
|
|
7
|
+
const candidates = mapMarketplaceEntries(registry, entry);
|
|
8
|
+
for (const candidate of candidates) {
|
|
9
|
+
const candidateId = candidate.id;
|
|
10
|
+
if (typeof candidateId !== 'string' || seen.has(candidateId)) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
seen.add(candidateId);
|
|
14
|
+
mapped.push(candidate);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return mapped;
|
|
18
|
+
}
|
|
19
|
+
function mapMarketplaceEntries(registry, entry) {
|
|
20
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const record = entry;
|
|
24
|
+
const rawName = readString(record, ['name', 'title', 'id']);
|
|
25
|
+
if (!rawName) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
if (registry.kind === 'skill') {
|
|
29
|
+
const skills = extractSkillRefs(record);
|
|
30
|
+
if (skills.length > 0) {
|
|
31
|
+
return skills
|
|
32
|
+
.map((skillRef) => mapBundledSkillEntry(registry, record, rawName, skillRef))
|
|
33
|
+
.filter((value) => value !== null);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const slug = slugify(rawName);
|
|
37
|
+
if (!slug) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const kind = registry.kind;
|
|
41
|
+
const description = stripHtml(readString(record, ['description']) ?? defaultDescription(kind, rawName), 320) ||
|
|
42
|
+
defaultDescription(kind, rawName);
|
|
43
|
+
const sourceUrl = resolveMarketplaceSourceUrl(registry, record);
|
|
44
|
+
const version = readString(record, ['version']);
|
|
45
|
+
const capabilities = dedupe(extractTags(record)
|
|
46
|
+
.concat(extractSkillRefs(record))
|
|
47
|
+
.concat(inferCapabilities(rawName, description)));
|
|
48
|
+
const compatibility = inferCompatibility(kind, capabilities);
|
|
49
|
+
return [{
|
|
50
|
+
id: `${kind}:${slug}`,
|
|
51
|
+
kind,
|
|
52
|
+
provider: registry.remote?.provider ?? 'github',
|
|
53
|
+
name: rawName.trim().slice(0, 120),
|
|
54
|
+
description,
|
|
55
|
+
capabilities,
|
|
56
|
+
compatibility,
|
|
57
|
+
source: registry.id,
|
|
58
|
+
install: {
|
|
59
|
+
kind: 'manual',
|
|
60
|
+
instructions: defaultInstallInstructions(kind),
|
|
61
|
+
...(sourceUrl ? { url: sourceUrl } : {})
|
|
62
|
+
},
|
|
63
|
+
adoptionSignal: toScore(record.adoptionSignal, registry.sourceType === 'vendor-feed' ? 66 : 58),
|
|
64
|
+
maintenanceSignal: toScore(record.maintenanceSignal, version ? 78 : 70),
|
|
65
|
+
provenanceSignal: toScore(record.provenanceSignal, registry.sourceType === 'vendor-feed' ? 88 : 74),
|
|
66
|
+
freshnessSignal: toScore(record.freshnessSignal, version ? 76 : 68),
|
|
67
|
+
securitySignals: {
|
|
68
|
+
knownVulnerabilities: toCount(record.knownVulnerabilities),
|
|
69
|
+
suspiciousPatterns: toCount(record.suspiciousPatterns),
|
|
70
|
+
injectionFindings: toCount(record.injectionFindings),
|
|
71
|
+
exfiltrationSignals: toCount(record.exfiltrationSignals),
|
|
72
|
+
integrityAlerts: toCount(record.integrityAlerts)
|
|
73
|
+
},
|
|
74
|
+
metadata: {
|
|
75
|
+
catalogType: kind === 'skill' ? 'skill' : 'plugin',
|
|
76
|
+
marketplaceRegistry: registry.id,
|
|
77
|
+
marketplaceSource: 'github',
|
|
78
|
+
rawVersion: version ?? 'unknown',
|
|
79
|
+
sourceConfidence: registry.sourceType === 'vendor-feed' ? 'official' : 'vetted-curated',
|
|
80
|
+
...(sourceUrl ? { githubUrl: sourceUrl } : {})
|
|
81
|
+
}
|
|
82
|
+
}];
|
|
83
|
+
}
|
|
84
|
+
function defaultDescription(kind, rawName) {
|
|
85
|
+
if (kind === 'skill') {
|
|
86
|
+
return `GitHub marketplace skill: ${rawName}.`;
|
|
87
|
+
}
|
|
88
|
+
return `GitHub marketplace Claude Code plugin: ${rawName}.`;
|
|
89
|
+
}
|
|
90
|
+
function defaultInstallInstructions(kind) {
|
|
91
|
+
if (kind === 'skill') {
|
|
92
|
+
return 'Review the linked GitHub skill and follow its repository installation instructions.';
|
|
93
|
+
}
|
|
94
|
+
return 'Review the linked GitHub Claude Code plugin and follow its repository installation instructions.';
|
|
95
|
+
}
|
|
96
|
+
function extractTags(record) {
|
|
97
|
+
const tags = [];
|
|
98
|
+
for (const field of ['keywords', 'tags']) {
|
|
99
|
+
const value = record[field];
|
|
100
|
+
if (!Array.isArray(value)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const item of value) {
|
|
104
|
+
if (typeof item === 'string' && item.trim().length > 0) {
|
|
105
|
+
tags.push(item.trim().toLowerCase());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const category = readString(record, ['category']);
|
|
110
|
+
if (category) {
|
|
111
|
+
tags.push(category.toLowerCase());
|
|
112
|
+
}
|
|
113
|
+
return dedupe(tags);
|
|
114
|
+
}
|
|
115
|
+
function extractSkillRefs(record) {
|
|
116
|
+
const value = record.skills;
|
|
117
|
+
if (!Array.isArray(value)) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
const refs = [];
|
|
121
|
+
for (const item of value) {
|
|
122
|
+
if (typeof item === 'string' && item.trim().length > 0) {
|
|
123
|
+
refs.push(item.trim().replace(/^\.?\//, '').toLowerCase());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return dedupe(refs);
|
|
127
|
+
}
|
|
128
|
+
function mapBundledSkillEntry(registry, record, rawName, skillRef) {
|
|
129
|
+
const slug = slugify(skillRef.split('/').at(-1) ?? skillRef);
|
|
130
|
+
if (!slug) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const description = stripHtml(readString(record, ['description']) ?? `GitHub marketplace skill from ${rawName}: ${skillRef}.`, 320) || `GitHub marketplace skill from ${rawName}: ${skillRef}.`;
|
|
134
|
+
const sourceUrl = resolveMarketplaceSourceUrl(registry, { ...record, source: `./${skillRef}` });
|
|
135
|
+
const version = readString(record, ['version']);
|
|
136
|
+
const capabilities = dedupe(extractTags(record).concat(extractSkillRefs(record)).concat(inferCapabilities(skillRef, description)));
|
|
137
|
+
return {
|
|
138
|
+
id: `skill:${slug}`,
|
|
139
|
+
kind: 'skill',
|
|
140
|
+
provider: registry.remote?.provider ?? 'github',
|
|
141
|
+
name: slug,
|
|
142
|
+
description,
|
|
143
|
+
capabilities,
|
|
144
|
+
compatibility: inferCompatibility('skill', capabilities),
|
|
145
|
+
source: registry.id,
|
|
146
|
+
install: {
|
|
147
|
+
kind: 'manual',
|
|
148
|
+
instructions: defaultInstallInstructions('skill'),
|
|
149
|
+
...(sourceUrl ? { url: sourceUrl } : {})
|
|
150
|
+
},
|
|
151
|
+
adoptionSignal: toScore(record.adoptionSignal, registry.sourceType === 'vendor-feed' ? 66 : 58),
|
|
152
|
+
maintenanceSignal: toScore(record.maintenanceSignal, version ? 78 : 70),
|
|
153
|
+
provenanceSignal: toScore(record.provenanceSignal, registry.sourceType === 'vendor-feed' ? 88 : 74),
|
|
154
|
+
freshnessSignal: toScore(record.freshnessSignal, version ? 76 : 68),
|
|
155
|
+
securitySignals: {
|
|
156
|
+
knownVulnerabilities: toCount(record.knownVulnerabilities),
|
|
157
|
+
suspiciousPatterns: toCount(record.suspiciousPatterns),
|
|
158
|
+
injectionFindings: toCount(record.injectionFindings),
|
|
159
|
+
exfiltrationSignals: toCount(record.exfiltrationSignals),
|
|
160
|
+
integrityAlerts: toCount(record.integrityAlerts)
|
|
161
|
+
},
|
|
162
|
+
metadata: {
|
|
163
|
+
catalogType: 'skill',
|
|
164
|
+
marketplacePlugin: rawName,
|
|
165
|
+
marketplaceRegistry: registry.id,
|
|
166
|
+
marketplaceSource: 'github',
|
|
167
|
+
rawVersion: version ?? 'unknown',
|
|
168
|
+
sourceConfidence: registry.sourceType === 'vendor-feed' ? 'official' : 'vetted-curated',
|
|
169
|
+
bundledSkillPath: skillRef,
|
|
170
|
+
...(sourceUrl ? { githubUrl: sourceUrl } : {})
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function inferCapabilities(name, description) {
|
|
175
|
+
const haystack = `${name} ${description}`.toLowerCase();
|
|
176
|
+
const capabilities = [];
|
|
177
|
+
if (/(browser|devtools|screenshot|web automation|playwright)/.test(haystack)) {
|
|
178
|
+
capabilities.push('browser-control');
|
|
179
|
+
}
|
|
180
|
+
if (/(security|sast|vulnerability|compliance|audit|auth)/.test(haystack)) {
|
|
181
|
+
capabilities.push('security');
|
|
182
|
+
}
|
|
183
|
+
if (/(database|postgres|redis|drizzle|sql|neo4j)/.test(haystack)) {
|
|
184
|
+
capabilities.push('data');
|
|
185
|
+
}
|
|
186
|
+
if (/(github|workflow|orchestration|automation|ci|cd|build)/.test(haystack)) {
|
|
187
|
+
capabilities.push('automation');
|
|
188
|
+
}
|
|
189
|
+
if (/(docs|documentation|markdown|diagram|mermaid|presentation)/.test(haystack)) {
|
|
190
|
+
capabilities.push('docs');
|
|
191
|
+
}
|
|
192
|
+
if (/(prompt|rag|evaluation|testing|review)/.test(haystack)) {
|
|
193
|
+
capabilities.push('guardrails');
|
|
194
|
+
}
|
|
195
|
+
if (capabilities.length === 0) {
|
|
196
|
+
capabilities.push('automation');
|
|
197
|
+
}
|
|
198
|
+
return dedupe(capabilities);
|
|
199
|
+
}
|
|
200
|
+
function inferCompatibility(kind, capabilities) {
|
|
201
|
+
const compatibility = kind === 'skill' ? ['claude-code', 'codex', 'general'] : ['claude', 'claude-code'];
|
|
202
|
+
if (capabilities.includes('browser-control') || capabilities.includes('automation')) {
|
|
203
|
+
compatibility.push('node');
|
|
204
|
+
}
|
|
205
|
+
if (capabilities.includes('data')) {
|
|
206
|
+
compatibility.push('database');
|
|
207
|
+
}
|
|
208
|
+
if (capabilities.includes('security')) {
|
|
209
|
+
compatibility.push('github');
|
|
210
|
+
}
|
|
211
|
+
return dedupe(compatibility);
|
|
212
|
+
}
|
|
213
|
+
function resolveMarketplaceSourceUrl(registry, record) {
|
|
214
|
+
const direct = sanitizeUrl(readString(record, ['homepage', 'repository', 'url']), TRUSTED_HOSTS);
|
|
215
|
+
if (direct) {
|
|
216
|
+
return direct;
|
|
217
|
+
}
|
|
218
|
+
const source = record.source;
|
|
219
|
+
if (typeof source === 'string' && source.trim().length > 0) {
|
|
220
|
+
const sanitized = sanitizeUrl(source, TRUSTED_HOSTS);
|
|
221
|
+
if (sanitized) {
|
|
222
|
+
return sanitized;
|
|
223
|
+
}
|
|
224
|
+
const repoContext = deriveGitHubRepoContext(registry.remote?.url);
|
|
225
|
+
if (repoContext) {
|
|
226
|
+
const normalized = source.trim().replace(/^\.?\//, '');
|
|
227
|
+
return `${repoContext.treeUrl}/${normalized}`;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (source && typeof source === 'object' && !Array.isArray(source)) {
|
|
231
|
+
const sourceRecord = source;
|
|
232
|
+
const repo = readString(sourceRecord, ['repo', 'repository']);
|
|
233
|
+
if (repo) {
|
|
234
|
+
return sanitizeUrl(`https://github.com/${repo}`, TRUSTED_HOSTS);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return deriveGitHubRepoContext(registry.remote?.url)?.repoUrl;
|
|
238
|
+
}
|
|
239
|
+
function deriveGitHubRepoContext(remoteUrl) {
|
|
240
|
+
if (!remoteUrl) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const url = new URL(remoteUrl);
|
|
245
|
+
if (url.hostname !== 'raw.githubusercontent.com') {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
const [owner, repo, branch] = url.pathname.split('/').filter(Boolean);
|
|
249
|
+
if (!owner || !repo || !branch) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
repoUrl: `https://github.com/${owner}/${repo}`,
|
|
254
|
+
treeUrl: `https://github.com/${owner}/${repo}/tree/${branch}`
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|