mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-23 20:05:12 +00:00
Skills rendering in collections
This commit is contained in:
@@ -1,30 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import path, { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname } from "path";
|
||||
import {
|
||||
parseCollectionYaml,
|
||||
extractMcpServers,
|
||||
AGENTS_DIR,
|
||||
AKA_INSTALL_URLS,
|
||||
COLLECTIONS_DIR,
|
||||
DOCS_DIR,
|
||||
INSTRUCTIONS_DIR,
|
||||
PROMPTS_DIR,
|
||||
repoBaseUrl,
|
||||
ROOT_FOLDER,
|
||||
SKILLS_DIR,
|
||||
TEMPLATES,
|
||||
vscodeInsidersInstallImage,
|
||||
vscodeInstallImage,
|
||||
} from "./constants.mjs";
|
||||
import {
|
||||
extractMcpServerConfigs,
|
||||
parseCollectionYaml,
|
||||
parseFrontmatter,
|
||||
parseSkillMetadata,
|
||||
} from "./yaml-parser.mjs";
|
||||
import {
|
||||
TEMPLATES,
|
||||
AKA_INSTALL_URLS,
|
||||
repoBaseUrl,
|
||||
vscodeInstallImage,
|
||||
vscodeInsidersInstallImage,
|
||||
ROOT_FOLDER,
|
||||
PROMPTS_DIR,
|
||||
AGENTS_DIR,
|
||||
SKILLS_DIR,
|
||||
COLLECTIONS_DIR,
|
||||
INSTRUCTIONS_DIR,
|
||||
DOCS_DIR,
|
||||
} from "./constants.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -55,14 +53,16 @@ async function loadMcpRegistryNames() {
|
||||
if (MCP_REGISTRY_SET) return MCP_REGISTRY_SET;
|
||||
|
||||
try {
|
||||
console.log('Fetching MCP registry from API...');
|
||||
console.log("Fetching MCP registry from API...");
|
||||
const allServers = [];
|
||||
let cursor = null;
|
||||
const apiUrl = 'https://api.mcp.github.com/v0.1/servers/';
|
||||
const apiUrl = "https://api.mcp.github.com/v0.1/servers/";
|
||||
|
||||
// Fetch all pages using cursor-based pagination
|
||||
do {
|
||||
const url = cursor ? `${apiUrl}?cursor=${encodeURIComponent(cursor)}` : apiUrl;
|
||||
const url = cursor
|
||||
? `${apiUrl}?cursor=${encodeURIComponent(cursor)}`
|
||||
: apiUrl;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -78,8 +78,9 @@ async function loadMcpRegistryNames() {
|
||||
if (serverName) {
|
||||
// Try to get displayName from GitHub metadata, fall back to server name
|
||||
const displayName =
|
||||
entry?.server?._meta?.["io.modelcontextprotocol.registry/publisher-provided"]?.github?.displayName ||
|
||||
serverName;
|
||||
entry?.server?._meta?.[
|
||||
"io.modelcontextprotocol.registry/publisher-provided"
|
||||
]?.github?.displayName || serverName;
|
||||
|
||||
allServers.push({
|
||||
name: serverName,
|
||||
@@ -431,19 +432,23 @@ function generateMcpServerLinks(servers, registryNames) {
|
||||
|
||||
// Match against both displayName and full name (case-insensitive)
|
||||
const serverNameLower = serverName.toLowerCase();
|
||||
const registryEntry = registryNames.find(
|
||||
(entry) => {
|
||||
const registryEntry = registryNames.find((entry) => {
|
||||
// Exact match on displayName or fullName
|
||||
if (entry.displayName === serverNameLower || entry.fullName === serverNameLower) {
|
||||
if (
|
||||
entry.displayName === serverNameLower ||
|
||||
entry.fullName === serverNameLower
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the serverName matches a part of the full name after a slash
|
||||
// e.g., "apify" matches "com.apify/apify-mcp-server"
|
||||
const nameParts = entry.fullName.split('/');
|
||||
const nameParts = entry.fullName.split("/");
|
||||
if (nameParts.length > 1 && nameParts[1]) {
|
||||
// Check if it matches the second part (after the slash)
|
||||
const secondPart = nameParts[1].replace('-mcp-server', '').replace('-mcp', '');
|
||||
const secondPart = nameParts[1]
|
||||
.replace("-mcp-server", "")
|
||||
.replace("-mcp", "");
|
||||
if (secondPart === serverNameLower) {
|
||||
return true;
|
||||
}
|
||||
@@ -451,8 +456,7 @@ function generateMcpServerLinks(servers, registryNames) {
|
||||
|
||||
// Check if serverName matches the displayName ignoring case
|
||||
return entry.displayName === serverNameLower;
|
||||
}
|
||||
);
|
||||
});
|
||||
const serverLabel = registryEntry
|
||||
? `[${serverName}](${`https://github.com/mcp/${registryEntry.name}`})`
|
||||
: serverName;
|
||||
@@ -489,9 +493,7 @@ function generateSkillsSection(skillsDir) {
|
||||
}
|
||||
|
||||
// Get all skill folders (directories)
|
||||
const skillFolders = fs
|
||||
.readdirSync(skillsDir)
|
||||
.filter((file) => {
|
||||
const skillFolders = fs.readdirSync(skillsDir).filter((file) => {
|
||||
const filePath = path.join(skillsDir, file);
|
||||
return fs.statSync(filePath).isDirectory();
|
||||
});
|
||||
@@ -768,7 +770,11 @@ function generateFeaturedCollectionsSection(collectionsDir) {
|
||||
* @param {string} collectionId - Collection ID
|
||||
* @param {{ name: string, displayName: string }[]} registryNames - Pre-loaded MCP registry names
|
||||
*/
|
||||
function generateCollectionReadme(collection, collectionId, registryNames = []) {
|
||||
function generateCollectionReadme(
|
||||
collection,
|
||||
collectionId,
|
||||
registryNames = []
|
||||
) {
|
||||
if (!collection || !collection.items) {
|
||||
return `# ${collectionId}\n\nCollection not found or invalid.`;
|
||||
}
|
||||
@@ -820,20 +826,23 @@ function generateCollectionReadme(collection, collectionId, registryNames = [])
|
||||
? "Instruction"
|
||||
: item.kind === "agent"
|
||||
? "Agent"
|
||||
: item.kind === "skill"
|
||||
? "Skill"
|
||||
: "Prompt";
|
||||
const link = `../${item.path}`;
|
||||
|
||||
// Create install badges for each item
|
||||
const badges = makeBadges(
|
||||
item.path,
|
||||
// Create install badges for each item (skills don't use chat install badges)
|
||||
const badgeType =
|
||||
item.kind === "instruction"
|
||||
? "instructions"
|
||||
: item.kind === "chat-mode"
|
||||
? "mode"
|
||||
: item.kind === "agent"
|
||||
? "agent"
|
||||
: "prompt"
|
||||
);
|
||||
: item.kind === "skill"
|
||||
? null
|
||||
: "prompt";
|
||||
const badges = badgeType ? makeBadges(item.path, badgeType) : "";
|
||||
|
||||
const usageDescription = item.usage
|
||||
? `${description} [see usage](#${title
|
||||
@@ -891,15 +900,21 @@ function buildCollectionRow({
|
||||
kind,
|
||||
registryNames = [],
|
||||
}) {
|
||||
const titleCell = badges
|
||||
? `[${title}](${link})<br />${badges}`
|
||||
: `[${title}](${link})`;
|
||||
|
||||
if (hasAgents) {
|
||||
// Only agents currently have MCP servers; future migration may extend to chat modes.
|
||||
const mcpServers =
|
||||
kind === "agent" ? extractMcpServerConfigs(filePath) : [];
|
||||
const mcpServerCell =
|
||||
mcpServers.length > 0 ? generateMcpServerLinks(mcpServers, registryNames) : "";
|
||||
return `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${usageDescription} | ${mcpServerCell} |\n`;
|
||||
mcpServers.length > 0
|
||||
? generateMcpServerLinks(mcpServers, registryNames)
|
||||
: "";
|
||||
return `| ${titleCell} | ${typeDisplay} | ${usageDescription} | ${mcpServerCell} |\n`;
|
||||
}
|
||||
return `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${usageDescription} |\n`;
|
||||
return `| ${titleCell} | ${typeDisplay} | ${usageDescription} |\n`;
|
||||
}
|
||||
|
||||
// Utility: write file only if content changed
|
||||
@@ -921,7 +936,13 @@ function writeFileIfChanged(filePath, content) {
|
||||
}
|
||||
|
||||
// Build per-category README content using existing generators, upgrading headings to H1
|
||||
function buildCategoryReadme(sectionBuilder, dirPath, headerLine, usageLine, registryNames = []) {
|
||||
function buildCategoryReadme(
|
||||
sectionBuilder,
|
||||
dirPath,
|
||||
headerLine,
|
||||
usageLine,
|
||||
registryNames = []
|
||||
) {
|
||||
const section = sectionBuilder(dirPath, registryNames);
|
||||
if (section && section.trim()) {
|
||||
// Upgrade the first markdown heading level from ## to # for standalone README files
|
||||
@@ -1077,7 +1098,9 @@ async function main() {
|
||||
writeFileIfChanged(mainReadmePath, readmeContent);
|
||||
console.log("Main README.md updated with featured collections");
|
||||
} else {
|
||||
console.warn("README.md not found, skipping featured collections update");
|
||||
console.warn(
|
||||
"README.md not found, skipping featured collections update"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log("No featured collections found to add to README.md");
|
||||
@@ -1095,4 +1118,3 @@ main().catch((error) => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user