Fixing some bugs in the update script

This commit is contained in:
Aaron Powell 2025-10-08 11:35:05 +11:00
parent b18b59a675
commit c1a51f45bf
2 changed files with 88 additions and 38 deletions

View File

@ -20,11 +20,11 @@ Curated collections of related prompts, instructions, and chat modes organized a
| [Clojure Interactive Programming](collections/clojure-interactive-programming.md) | Tools for REPL-first Clojure workflows featuring Clojure instructions, the interactive programming chat mode and supporting guidance. | 3 items | clojure, repl, interactive-programming | | [Clojure Interactive Programming](collections/clojure-interactive-programming.md) | Tools for REPL-first Clojure workflows featuring Clojure instructions, the interactive programming chat mode and supporting guidance. | 3 items | clojure, repl, interactive-programming |
| [Database & Data Management](collections/database-data-management.md) | Database administration, SQL optimization, and data management tools for PostgreSQL, SQL Server, and general database development best practices. | 8 items | database, sql, postgresql, sql-server, dba, optimization, queries, data-management | | [Database & Data Management](collections/database-data-management.md) | Database administration, SQL optimization, and data management tools for PostgreSQL, SQL Server, and general database development best practices. | 8 items | database, sql, postgresql, sql-server, dba, optimization, queries, data-management |
| [DevOps On-Call](collections/devops-oncall.md) | A focused set of prompts, instructions, and a chat mode to help triage incidents and respond quickly with DevOps tools and Azure resources. | 5 items | devops, incident-response, oncall, azure | | [DevOps On-Call](collections/devops-oncall.md) | A focused set of prompts, instructions, and a chat mode to help triage incidents and respond quickly with DevOps tools and Azure resources. | 5 items | devops, incident-response, oncall, azure |
| [Tasks by microsoft/edge-ai](collections/edge-ai-tasks.md) | Task Researcher and Task Planner for intermediate to expert users and large codebases - Brought to you by microsoft/edge-ai | 3 items | architecture, planning, research, tasks, implementation |
| [Frontend Web Development](collections/frontend-web-dev.md) | Essential prompts, instructions, and chat modes for modern frontend web development including React, Angular, Vue, TypeScript, and CSS frameworks. | 11 items | frontend, web, react, typescript, javascript, css, html, angular, vue | | [Frontend Web Development](collections/frontend-web-dev.md) | Essential prompts, instructions, and chat modes for modern frontend web development including React, Angular, Vue, TypeScript, and CSS frameworks. | 11 items | frontend, web, react, typescript, javascript, css, html, angular, vue |
| [Java Development](collections/java-development.md) | Comprehensive collection of prompts and instructions for Java development including Spring Boot, Quarkus, testing, documentation, and best practices. | 12 items | java, springboot, quarkus, jpa, junit, javadoc | | [Java Development](collections/java-development.md) | Comprehensive collection of prompts and instructions for Java development including Spring Boot, Quarkus, testing, documentation, and best practices. | 12 items | java, springboot, quarkus, jpa, junit, javadoc |
| [Power Apps Code Apps Development](collections/power-apps-code-apps.md) | Complete toolkit for Power Apps Code Apps development including project scaffolding, development standards, and expert guidance for building code-first applications with Power Platform integration. | 3 items | power-apps, power-platform, typescript, react, code-apps, dataverse, connectors | | [Power Apps Code Apps Development](collections/power-apps-code-apps.md) | Complete toolkit for Power Apps Code Apps development including project scaffolding, development standards, and expert guidance for building code-first applications with Power Platform integration. | 3 items | power-apps, power-platform, typescript, react, code-apps, dataverse, connectors |
| [Project Planning & Management](collections/project-planning.md) | Tools and guidance for software project planning, feature breakdown, epic management, implementation planning, and task organization for development teams. | 17 items | planning, project-management, epic, feature, implementation, task, architecture, technical-spike | | [Project Planning & Management](collections/project-planning.md) | Tools and guidance for software project planning, feature breakdown, epic management, implementation planning, and task organization for development teams. | 17 items | planning, project-management, epic, feature, implementation, task, architecture, technical-spike |
| [Security & Code Quality](collections/security-best-practices.md) | Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications. | 6 items | security, accessibility, performance, code-quality, owasp, a11y, optimization, best-practices | | [Security & Code Quality](collections/security-best-practices.md) | Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications. | 6 items | security, accessibility, performance, code-quality, owasp, a11y, optimization, best-practices |
| [Tasks by microsoft/edge-ai](collections/edge-ai-tasks.md) | Task Researcher and Task Planner for intermediate to expert users and large codebases - Brought to you by microsoft/edge-ai | 3 items | architecture, planning, research, tasks, implementation |
| [Technical Spike](collections/technical-spike.md) | Tools for creation, management and research of technical spikes to reduce unknowns and assumptions before proceeding to specification and implementation of solutions. | 2 items | technical-spike, assumption-testing, validation, research | | [Technical Spike](collections/technical-spike.md) | Tools for creation, management and research of technical spikes to reduce unknowns and assumptions before proceeding to specification and implementation of solutions. | 2 items | technical-spike, assumption-testing, validation, research |
| [Testing & Test Automation](collections/testing-automation.md) | Comprehensive collection for writing tests, test automation, and test-driven development including unit tests, integration tests, and end-to-end testing strategies. | 11 items | testing, tdd, automation, unit-tests, integration, playwright, jest, nunit | | [Testing & Test Automation](collections/testing-automation.md) | Comprehensive collection for writing tests, test automation, and test-driven development including unit tests, integration tests, and end-to-end testing strategies. | 11 items | testing, tdd, automation, unit-tests, integration, playwright, jest, nunit |

View File

@ -142,7 +142,10 @@ function extractTitle(filePath) {
// Track code blocks to ignore headings inside them // Track code blocks to ignore headings inside them
if (frontmatterEnded2) { if (frontmatterEnded2) {
if (line.trim().startsWith("```") || line.trim().startsWith("````")) { if (
line.trim().startsWith("```") ||
line.trim().startsWith("````")
) {
inCodeBlock = !inCodeBlock; inCodeBlock = !inCodeBlock;
continue; continue;
} }
@ -156,7 +159,10 @@ function extractTitle(filePath) {
// No frontmatter, look for first heading (but not in code blocks) // No frontmatter, look for first heading (but not in code blocks)
let inCodeBlock = false; let inCodeBlock = false;
for (const line of lines) { for (const line of lines) {
if (line.trim().startsWith("```") || line.trim().startsWith("````")) { if (
line.trim().startsWith("```") ||
line.trim().startsWith("````")
) {
inCodeBlock = !inCodeBlock; inCodeBlock = !inCodeBlock;
continue; continue;
} }
@ -260,7 +266,9 @@ function extractDescription(filePath) {
let description = descriptionMatch[1]; let description = descriptionMatch[1];
// Check if the description is wrapped in single quotes and handle escaped quotes // 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) { if (singleQuoteMatch) {
// Replace escaped single quotes ('') with single quotes (') // Replace escaped single quotes ('') with single quotes (')
description = singleQuoteMatch[1].replace(/''/g, "'"); description = singleQuoteMatch[1].replace(/''/g, "'");
@ -308,8 +316,12 @@ const AKA_INSTALL_URLS = {
function makeBadges(link, type) { function makeBadges(link, type) {
const aka = AKA_INSTALL_URLS[type] || AKA_INSTALL_URLS.instructions; const aka = AKA_INSTALL_URLS[type] || AKA_INSTALL_URLS.instructions;
const vscodeUrl = `${aka}?url=${encodeURIComponent(`vscode:chat-${type}/install?url=${repoBaseUrl}/${link}`)}`; const vscodeUrl = `${aka}?url=${encodeURIComponent(
const insidersUrl = `${aka}?url=${encodeURIComponent(`vscode-insiders:chat-${type}/install?url=${repoBaseUrl}/${link}`)}`; `vscode:chat-${type}/install?url=${repoBaseUrl}/${link}`
)}`;
const insidersUrl = `${aka}?url=${encodeURIComponent(
`vscode-insiders:chat-${type}/install?url=${repoBaseUrl}/${link}`
)}`;
return `[![Install in VS Code](${vscodeInstallImage})](${vscodeUrl})<br />[![Install in VS Code Insiders](${vscodeInsidersInstallImage})](${insidersUrl})`; return `[![Install in VS Code](${vscodeInstallImage})](${vscodeUrl})<br />[![Install in VS Code Insiders](${vscodeInsidersInstallImage})](${insidersUrl})`;
} }
@ -405,8 +417,7 @@ function generatePromptsSection(promptsDir) {
} }
// Create table header // Create table header
let promptsContent = let promptsContent = "| Title | Description |\n| ----- | ----------- |\n";
"| Title | Description |\n| ----- | ----------- |\n";
// Generate table rows for each prompt file // Generate table rows for each prompt file
for (const entry of promptEntries) { for (const entry of promptEntries) {
@ -462,8 +473,7 @@ function generateChatModesSection(chatmodesDir) {
} }
// Create table header // Create table header
let chatmodesContent = let chatmodesContent = "| Title | Description |\n| ----- | ----------- |\n";
"| Title | Description |\n| ----- | ----------- |\n";
// Generate table rows for each chat mode file // Generate table rows for each chat mode file
for (const entry of chatmodeEntries) { for (const entry of chatmodeEntries) {
@ -502,19 +512,22 @@ function generateCollectionsSection(collectionsDir) {
.filter((file) => file.endsWith(".collection.yml")); .filter((file) => file.endsWith(".collection.yml"));
// Map collection files to objects with name for sorting // Map collection files to objects with name for sorting
const collectionEntries = collectionFiles.map((file) => { const collectionEntries = collectionFiles
const filePath = path.join(collectionsDir, file); .map((file) => {
const collection = parseCollectionYaml(filePath); const filePath = path.join(collectionsDir, file);
const collection = parseCollectionYaml(filePath);
if (!collection) { if (!collection) {
console.warn(`Failed to parse collection: ${file}`); console.warn(`Failed to parse collection: ${file}`);
return null; return null;
} }
const collectionId = collection.id || path.basename(file, ".collection.yml"); const collectionId =
const name = collection.name || collectionId; collection.id || path.basename(file, ".collection.yml");
return { file, filePath, collection, collectionId, name }; const name = collection.name || collectionId;
}).filter(entry => entry !== null); // Remove failed parses return { file, filePath, collection, collectionId, name };
})
.filter((entry) => entry !== null); // Remove failed parses
// Sort by name alphabetically // Sort by name alphabetically
collectionEntries.sort((a, b) => a.name.localeCompare(b.name)); collectionEntries.sort((a, b) => a.name.localeCompare(b.name));
@ -584,13 +597,23 @@ function generateCollectionReadme(collection, collectionId) {
const title = extractTitle(filePath); const title = extractTitle(filePath);
const description = extractDescription(filePath) || "No description"; const description = extractDescription(filePath) || "No description";
const typeDisplay = item.kind === "chat-mode" ? "Chat Mode" : const typeDisplay =
item.kind === "instruction" ? "Instruction" : "Prompt"; item.kind === "chat-mode"
? "Chat Mode"
: item.kind === "instruction"
? "Instruction"
: "Prompt";
const link = `../${item.path}`; const link = `../${item.path}`;
// Create install badges for each item // Create install badges for each item
const badges = makeBadges(item.path, item.kind === "instruction" ? "instructions" : const badges = makeBadges(
item.kind === "chat-mode" ? "mode" : "prompt"); item.path,
item.kind === "instruction"
? "instructions"
: item.kind === "chat-mode"
? "mode"
: "prompt"
);
const usageDescription = item.usage const usageDescription = item.usage
? `${description} [see usage](#${title ? `${description} [see usage](#${title
@ -601,8 +624,11 @@ function generateCollectionReadme(collection, collectionId) {
content += `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${usageDescription} |\n`; content += `| [${title}](${link})<br />${badges} | ${typeDisplay} | ${usageDescription} |\n`;
// Generate Usage section for each collection // Generate Usage section for each collection
if (item.usage && item.usage.trim()) { if (item.usage && item.usage.trim()) {
collectionUsageContent.push(`### ${title}\n\n${item.usage.trim()}\n\n---\n\n`); collectionUsageContent.push(
`### ${title}\n\n${item.usage.trim()}\n\n---\n\n`
);
} }
}
// Append the usage section if any items had usage defined // Append the usage section if any items had usage defined
if (collectionUsageContent.length > 0) { if (collectionUsageContent.length > 0) {
@ -611,10 +637,11 @@ function generateCollectionReadme(collection, collectionId) {
content += "\n---\n"; content += "\n---\n";
} }
// Optional badge note at the end if show_badge is true // Optional badge note at the end if show_badge is true
if (collection.display?.show_badge) { if (collection.display?.show_badge) {
content += `*This collection includes ${items.length} curated items for ${name.toLowerCase()}.*`; content += `*This collection includes ${
items.length
} curated items for ${name.toLowerCase()}.*`;
} }
return content; return content;
@ -626,12 +653,16 @@ function writeFileIfChanged(filePath, content) {
if (exists) { if (exists) {
const original = fs.readFileSync(filePath, "utf8"); const original = fs.readFileSync(filePath, "utf8");
if (original === content) { 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; return;
} }
} }
fs.writeFileSync(filePath, content); 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 // Build per-category README content using existing generators, upgrading headings to H1
@ -655,10 +686,16 @@ try {
const collectionsDir = path.join(__dirname, "collections"); const collectionsDir = path.join(__dirname, "collections");
// Compose headers for standalone files by converting section headers to H1 // 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 promptsHeader = TEMPLATES.promptsSection.replace(/^##\s/m, "# ");
const chatmodesHeader = TEMPLATES.chatmodesSection.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( const instructionsReadme = buildCategoryReadme(
generateInstructionsSection, generateInstructionsSection,
@ -688,10 +725,19 @@ try {
); );
// Write category outputs // 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.prompts.md"), promptsReadme);
writeFileIfChanged(path.join(__dirname, "README.chatmodes.md"), chatmodesReadme); writeFileIfChanged(
writeFileIfChanged(path.join(__dirname, "README.collections.md"), collectionsReadme); path.join(__dirname, "README.chatmodes.md"),
chatmodesReadme
);
writeFileIfChanged(
path.join(__dirname, "README.collections.md"),
collectionsReadme
);
// Generate individual collection README files // Generate individual collection README files
if (fs.existsSync(collectionsDir)) { if (fs.existsSync(collectionsDir)) {
@ -706,8 +752,12 @@ try {
const collection = parseCollectionYaml(filePath); const collection = parseCollectionYaml(filePath);
if (collection) { if (collection) {
const collectionId = collection.id || path.basename(file, ".collection.yml"); const collectionId =
const readmeContent = generateCollectionReadme(collection, collectionId); collection.id || path.basename(file, ".collection.yml");
const readmeContent = generateCollectionReadme(
collection,
collectionId
);
const readmeFile = path.join(collectionsDir, `${collectionId}.md`); const readmeFile = path.join(collectionsDir, `${collectionId}.md`);
writeFileIfChanged(readmeFile, readmeContent); writeFileIfChanged(readmeFile, readmeContent);
} }