mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-22 19:35:13 +00:00
Collections Improvement with Tasks Collection POC (#270)
* add yaml block support * Add usage Section to Collections * new edge ai tasks collection with usage example from PR https://github.com/github/awesome-copilot/pull/159 * Update collections/edge-ai-tasks.collection.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update collections/edge-ai-tasks.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update collections/edge-ai-tasks.collection.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update collections/edge-ai-tasks.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fixing some bugs in the update script --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Aaron Powell <me@aaron-powell.com>
This commit is contained in:
142
update-readme.js
142
update-readme.js
@@ -142,7 +142,10 @@ function extractTitle(filePath) {
|
||||
|
||||
// Track code blocks to ignore headings inside them
|
||||
if (frontmatterEnded2) {
|
||||
if (line.trim().startsWith("```") || line.trim().startsWith("````")) {
|
||||
if (
|
||||
line.trim().startsWith("```") ||
|
||||
line.trim().startsWith("````")
|
||||
) {
|
||||
inCodeBlock = !inCodeBlock;
|
||||
continue;
|
||||
}
|
||||
@@ -156,7 +159,10 @@ function extractTitle(filePath) {
|
||||
// No frontmatter, look for first heading (but not in code blocks)
|
||||
let inCodeBlock = false;
|
||||
for (const line of lines) {
|
||||
if (line.trim().startsWith("```") || line.trim().startsWith("````")) {
|
||||
if (
|
||||
line.trim().startsWith("```") ||
|
||||
line.trim().startsWith("````")
|
||||
) {
|
||||
inCodeBlock = !inCodeBlock;
|
||||
continue;
|
||||
}
|
||||
@@ -260,7 +266,9 @@ function extractDescription(filePath) {
|
||||
let description = descriptionMatch[1];
|
||||
|
||||
// Check if the description is wrapped in single quotes and handle escaped quotes
|
||||
const singleQuoteMatch = line.match(/^description:\s*'(.+?)'\s*$/);
|
||||
const singleQuoteMatch = line.match(
|
||||
/^description:\s*'(.+?)'\s*$/
|
||||
);
|
||||
if (singleQuoteMatch) {
|
||||
// Replace escaped single quotes ('') with single quotes (')
|
||||
description = singleQuoteMatch[1].replace(/''/g, "'");
|
||||
@@ -308,8 +316,12 @@ const AKA_INSTALL_URLS = {
|
||||
function makeBadges(link, type) {
|
||||
const aka = AKA_INSTALL_URLS[type] || AKA_INSTALL_URLS.instructions;
|
||||
|
||||
const vscodeUrl = `${aka}?url=${encodeURIComponent(`vscode:chat-${type}/install?url=${repoBaseUrl}/${link}`)}`;
|
||||
const insidersUrl = `${aka}?url=${encodeURIComponent(`vscode-insiders:chat-${type}/install?url=${repoBaseUrl}/${link}`)}`;
|
||||
const vscodeUrl = `${aka}?url=${encodeURIComponent(
|
||||
`vscode:chat-${type}/install?url=${repoBaseUrl}/${link}`
|
||||
)}`;
|
||||
const insidersUrl = `${aka}?url=${encodeURIComponent(
|
||||
`vscode-insiders:chat-${type}/install?url=${repoBaseUrl}/${link}`
|
||||
)}`;
|
||||
|
||||
return `[](${vscodeUrl})<br />[](${insidersUrl})`;
|
||||
}
|
||||
@@ -405,8 +417,7 @@ function generatePromptsSection(promptsDir) {
|
||||
}
|
||||
|
||||
// Create table header
|
||||
let promptsContent =
|
||||
"| Title | Description |\n| ----- | ----------- |\n";
|
||||
let promptsContent = "| Title | Description |\n| ----- | ----------- |\n";
|
||||
|
||||
// Generate table rows for each prompt file
|
||||
for (const entry of promptEntries) {
|
||||
@@ -462,8 +473,7 @@ function generateChatModesSection(chatmodesDir) {
|
||||
}
|
||||
|
||||
// Create table header
|
||||
let chatmodesContent =
|
||||
"| Title | Description |\n| ----- | ----------- |\n";
|
||||
let chatmodesContent = "| Title | Description |\n| ----- | ----------- |\n";
|
||||
|
||||
// Generate table rows for each chat mode file
|
||||
for (const entry of chatmodeEntries) {
|
||||
@@ -502,19 +512,22 @@ function generateCollectionsSection(collectionsDir) {
|
||||
.filter((file) => file.endsWith(".collection.yml"));
|
||||
|
||||
// Map collection files to objects with name for sorting
|
||||
const collectionEntries = collectionFiles.map((file) => {
|
||||
const filePath = path.join(collectionsDir, file);
|
||||
const collection = parseCollectionYaml(filePath);
|
||||
const collectionEntries = collectionFiles
|
||||
.map((file) => {
|
||||
const filePath = path.join(collectionsDir, file);
|
||||
const collection = parseCollectionYaml(filePath);
|
||||
|
||||
if (!collection) {
|
||||
console.warn(`Failed to parse collection: ${file}`);
|
||||
return null;
|
||||
}
|
||||
if (!collection) {
|
||||
console.warn(`Failed to parse collection: ${file}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const collectionId = collection.id || path.basename(file, ".collection.yml");
|
||||
const name = collection.name || collectionId;
|
||||
return { file, filePath, collection, collectionId, name };
|
||||
}).filter(entry => entry !== null); // Remove failed parses
|
||||
const collectionId =
|
||||
collection.id || path.basename(file, ".collection.yml");
|
||||
const name = collection.name || collectionId;
|
||||
return { file, filePath, collection, collectionId, name };
|
||||
})
|
||||
.filter((entry) => entry !== null); // Remove failed parses
|
||||
|
||||
// Sort by name alphabetically
|
||||
collectionEntries.sort((a, b) => a.name.localeCompare(b.name));
|
||||
@@ -566,6 +579,9 @@ function generateCollectionReadme(collection, collectionId) {
|
||||
content += `## Items in this Collection\n\n`;
|
||||
content += `| Title | Type | Description |\n| ----- | ---- | ----------- |\n`;
|
||||
|
||||
let collectionUsageHeader = "## Collection Usage\n\n";
|
||||
let collectionUsageContent = [];
|
||||
|
||||
// Sort items based on display.ordering setting
|
||||
const items = [...collection.items];
|
||||
if (collection.display?.ordering === "alpha") {
|
||||
@@ -580,19 +596,52 @@ function generateCollectionReadme(collection, collectionId) {
|
||||
const filePath = path.join(__dirname, item.path);
|
||||
const title = extractTitle(filePath);
|
||||
const description = extractDescription(filePath) || "No description";
|
||||
const typeDisplay = item.kind === "chat-mode" ? "Chat Mode" :
|
||||
item.kind === "instruction" ? "Instruction" : "Prompt";
|
||||
|
||||
const typeDisplay =
|
||||
item.kind === "chat-mode"
|
||||
? "Chat Mode"
|
||||
: item.kind === "instruction"
|
||||
? "Instruction"
|
||||
: "Prompt";
|
||||
const link = `../${item.path}`;
|
||||
|
||||
// Create install badges for each item
|
||||
const badges = makeBadges(item.path, item.kind === "instruction" ? "instructions" :
|
||||
item.kind === "chat-mode" ? "mode" : "prompt");
|
||||
const badges = makeBadges(
|
||||
item.path,
|
||||
item.kind === "instruction"
|
||||
? "instructions"
|
||||
: item.kind === "chat-mode"
|
||||
? "mode"
|
||||
: "prompt"
|
||||
);
|
||||
|
||||
content += `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${description} |\n`;
|
||||
const usageDescription = item.usage
|
||||
? `${description} [see usage](#${title
|
||||
.replace(/\s+/g, "-")
|
||||
.toLowerCase()})`
|
||||
: description;
|
||||
|
||||
content += `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${usageDescription} |\n`;
|
||||
// Generate Usage section for each collection
|
||||
if (item.usage && item.usage.trim()) {
|
||||
collectionUsageContent.push(
|
||||
`### ${title}\n\n${item.usage.trim()}\n\n---\n\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the usage section if any items had usage defined
|
||||
if (collectionUsageContent.length > 0) {
|
||||
content += `\n${collectionUsageHeader}${collectionUsageContent.join("")}`;
|
||||
} else if (collection.display?.show_badge) {
|
||||
content += "\n---\n";
|
||||
}
|
||||
|
||||
// Optional badge note at the end if show_badge is true
|
||||
if (collection.display?.show_badge) {
|
||||
content += `\n---\n*This collection includes ${items.length} curated items for ${name.toLowerCase()}.*`;
|
||||
content += `*This collection includes ${
|
||||
items.length
|
||||
} curated items for ${name.toLowerCase()}.*`;
|
||||
}
|
||||
|
||||
return content;
|
||||
@@ -604,12 +653,16 @@ function writeFileIfChanged(filePath, content) {
|
||||
if (exists) {
|
||||
const original = fs.readFileSync(filePath, "utf8");
|
||||
if (original === content) {
|
||||
console.log(`${path.basename(filePath)} is already up to date. No changes needed.`);
|
||||
console.log(
|
||||
`${path.basename(filePath)} is already up to date. No changes needed.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(filePath, content);
|
||||
console.log(`${path.basename(filePath)} ${exists ? "updated" : "created"} successfully!`);
|
||||
console.log(
|
||||
`${path.basename(filePath)} ${exists ? "updated" : "created"} successfully!`
|
||||
);
|
||||
}
|
||||
|
||||
// Build per-category README content using existing generators, upgrading headings to H1
|
||||
@@ -633,10 +686,16 @@ try {
|
||||
const collectionsDir = path.join(__dirname, "collections");
|
||||
|
||||
// Compose headers for standalone files by converting section headers to H1
|
||||
const instructionsHeader = TEMPLATES.instructionsSection.replace(/^##\s/m, "# ");
|
||||
const instructionsHeader = TEMPLATES.instructionsSection.replace(
|
||||
/^##\s/m,
|
||||
"# "
|
||||
);
|
||||
const promptsHeader = TEMPLATES.promptsSection.replace(/^##\s/m, "# ");
|
||||
const chatmodesHeader = TEMPLATES.chatmodesSection.replace(/^##\s/m, "# ");
|
||||
const collectionsHeader = TEMPLATES.collectionsSection.replace(/^##\s/m, "# ");
|
||||
const collectionsHeader = TEMPLATES.collectionsSection.replace(
|
||||
/^##\s/m,
|
||||
"# "
|
||||
);
|
||||
|
||||
const instructionsReadme = buildCategoryReadme(
|
||||
generateInstructionsSection,
|
||||
@@ -666,10 +725,19 @@ try {
|
||||
);
|
||||
|
||||
// Write category outputs
|
||||
writeFileIfChanged(path.join(__dirname, "README.instructions.md"), instructionsReadme);
|
||||
writeFileIfChanged(
|
||||
path.join(__dirname, "README.instructions.md"),
|
||||
instructionsReadme
|
||||
);
|
||||
writeFileIfChanged(path.join(__dirname, "README.prompts.md"), promptsReadme);
|
||||
writeFileIfChanged(path.join(__dirname, "README.chatmodes.md"), chatmodesReadme);
|
||||
writeFileIfChanged(path.join(__dirname, "README.collections.md"), collectionsReadme);
|
||||
writeFileIfChanged(
|
||||
path.join(__dirname, "README.chatmodes.md"),
|
||||
chatmodesReadme
|
||||
);
|
||||
writeFileIfChanged(
|
||||
path.join(__dirname, "README.collections.md"),
|
||||
collectionsReadme
|
||||
);
|
||||
|
||||
// Generate individual collection README files
|
||||
if (fs.existsSync(collectionsDir)) {
|
||||
@@ -684,8 +752,12 @@ try {
|
||||
const collection = parseCollectionYaml(filePath);
|
||||
|
||||
if (collection) {
|
||||
const collectionId = collection.id || path.basename(file, ".collection.yml");
|
||||
const readmeContent = generateCollectionReadme(collection, collectionId);
|
||||
const collectionId =
|
||||
collection.id || path.basename(file, ".collection.yml");
|
||||
const readmeContent = generateCollectionReadme(
|
||||
collection,
|
||||
collectionId
|
||||
);
|
||||
const readmeFile = path.join(collectionsDir, `${collectionId}.md`);
|
||||
writeFileIfChanged(readmeFile, readmeContent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user