#!/usr/bin/env node const fs = require("fs"); const path = require("path"); // Template sections for the README const TEMPLATES = { header: `# πŸ€– Awesome GitHub Copilot Customizations [![Powered by Awesome Copilot](https://img.shields.io/badge/Powered_by-Awesome_Copilot-blue?logo=githubcopilot)](https://aka.ms/awesome-github-copilot) [![All Contributors](https://img.shields.io/badge/all_contributors-86-orange.svg?style=flat-square)](#contributors-) Enhance your GitHub Copilot experience with community-contributed [instructions](#-custom-instructions), [prompts](#-reusable-prompts), and [chat modes](#-custom-chat-modes). Get consistent AI assistance that follows your team's coding standards and project requirements.
🎯 GitHub Copilot Customization Features GitHub Copilot provides three main ways to customize AI responses and tailor assistance to your specific workflows, team guidelines, and project requirements: | **🧩 [Custom Chat Modes](#-custom-chat-modes)** | **🎯 [Reusable Prompts](#-reusable-prompts)** | **πŸ“‹ [Custom Instructions](#-custom-instructions)** | | --- | --- | --- | | Define chat behavior, available tools, and codebase interaction patterns within specific boundaries for each request

**Benefits:**
β€’ Context-aware assistance
β€’ Tool configuration
β€’ Role-specific workflows | Create reusable, standalone prompts for specific tasks. Describe *what* should be done with optional task-specific guidelines

**Benefits:**
β€’ Eliminate repetitive prompt writing
β€’ Shareable across teams
β€’ Support for variables and dependencies | Define common guidelines for tasks like code generation, reviews, and commit messages. Describe *how* tasks should be performed

**Benefits:**
β€’ Automatic inclusion in every chat request
β€’ Repository-wide consistency
β€’ Multiple implementation options | > **πŸ’‘ Pro Tip:** Custom instructions only affect Copilot Chat (not inline code completions). You can combine all three customization types - use custom instructions for general guidelines, prompt files for specific tasks, and chat modes to control the interaction context.
πŸ“ Contributing We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details on how to submit new instructions and prompts.
`, instructionsSection: `## πŸ“‹ Custom Instructions Team and project-specific instructions to enhance GitHub Copilot's behavior for specific technologies and coding practices.`, instructionsUsage: `### How to Use Custom Instructions **To Install:** - Click the **VS Code** or **VS Code Insiders** install button for the instruction you want to use - Download the \`*.instructions.md\` file and manually add it to your project's instruction collection **To Use/Apply:** - Copy these instructions to your \`.github/copilot-instructions.md\` file in your workspace - Create task-specific \`.github/.instructions.md\` files in your workspace's \`.github/instructions\` folder - Instructions automatically apply to Copilot behavior once installed in your workspace`, promptsSection: `## 🎯 Reusable Prompts Ready-to-use prompt templates for specific development scenarios and tasks, defining prompt text with a specific mode, model, and available set of tools.`, promptsUsage: `### How to Use Reusable Prompts **To Install:** - Click the **VS Code** or **VS Code Insiders** install button for the prompt you want to use - Download the \`*.prompt.md\` file and manually add it to your prompt collection **To Run/Execute:** - Use \`/prompt-name\` in VS Code chat after installation - Run the \`Chat: Run Prompt\` command from the Command Palette - Hit the run button while you have a prompt file open in VS Code`, chatmodesSection: `## 🧩 Custom Chat Modes Custom chat modes define specific behaviors and tools for GitHub Copilot Chat, enabling enhanced context-aware assistance for particular tasks or workflows.`, chatmodesUsage: `### How to Use Custom Chat Modes **To Install:** - Click the **VS Code** or **VS Code Insiders** install button for the chat mode you want to use - Download the \`*.chatmode.md\` file and manually install it in VS Code using the Command Palette **To Activate/Use:** - Import the chat mode configuration into your VS Code settings - Access the installed chat modes through the VS Code Chat interface - Select the desired chat mode from the available options in VS Code Chat`, footer: `## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Aaron Powell
Aaron Powell

πŸ’» 🚧 πŸ“† πŸ“£
Muhammad Ubaid Raza
Muhammad Ubaid Raza

πŸ’»
Harald Kirschner
Harald Kirschner

πŸ’»
Matteo Bianchi
Matteo Bianchi

πŸ’»
Aung Myo Kyaw
Aung Myo Kyaw

πŸ’»
Daniel Scott-Raynsford
Daniel Scott-Raynsford

πŸ’»
Burke Holland
Burke Holland

πŸ’»
Peter StrΓΆmberg
Peter StrΓΆmberg

πŸ’»
Daniel Meppiel
Daniel Meppiel

πŸ’»
James Montemagno
James Montemagno

πŸ’»
Vamshi Verma
Vamshi Verma

πŸ’»
Yohan Lasorsa
Yohan Lasorsa

πŸ’»
Oren Me
Oren Me

πŸ’»
Mike Rousos
Mike Rousos

πŸ’»
Guilherme do Amaral Alves
Guilherme do Amaral Alves

πŸ’»
Troy Simeon Taylor
Troy Simeon Taylor

πŸ’»
Ambily
Ambily

πŸ’»
Tugdual Grall
Tugdual Grall

πŸ’»
Tianqi Zhang
Tianqi Zhang

πŸ’»
Shubham Gaikwad
Shubham Gaikwad

πŸ’»
Saul Dolgin
Saul Dolgin

πŸ’»
NULLchimp
NULLchimp

πŸ’»
Matt Vevang
Matt Vevang

πŸ’»
Justin Yoo
Justin Yoo

πŸ’»
Gisela Torres
Gisela Torres

πŸ’»
Debbie O'Brien
Debbie O'Brien

πŸ’»
Allen Greaves
Allen Greaves

πŸ’»
Amelia Payne
Amelia Payne

πŸ’»
Sebastien DEGODEZ
Sebastien DEGODEZ

πŸ’»
Sebastian GrΓ€f
Sebastian GrΓ€f

πŸ’»
Salih İbrahimbaş
Salih İbrahimbaş

πŸ’»
Robert Altman
Robert Altman

πŸ’»
Rob Simpson
Rob Simpson

πŸ’»
Rick Smit
Rick Smit

πŸ’»
Peter Smulovics
Peter Smulovics

πŸ’»
Peli de Halleux
Peli de Halleux

πŸ’»
Paulo Morgado
Paulo Morgado

πŸ’»
Nick Taylor
Nick Taylor

πŸ’»
Mike Parker
Mike Parker

πŸ’»
Mike Kistler
Mike Kistler

πŸ’»
Michael Fairchild
Michael Fairchild

πŸ’»
Michael A. Volz (Flynn)
Michael A. Volz (Flynn)

πŸ’»
4regab
4regab

πŸ’»
Theo van Kraay
Theo van Kraay

πŸ’»
Troy Witthoeft (glsauto)
Troy Witthoeft (glsauto)

πŸ’»
TΓ i LΓͺ
TΓ i LΓͺ

πŸ’»
Udaya Veeramreddygari
Udaya Veeramreddygari

πŸ’»
Waren Gonzaga
Waren Gonzaga

πŸ’»
Will 保ε“₯
Will 保ε“₯

πŸ’»
Yuki Omoto
Yuki Omoto

πŸ’»
Meii
Meii

πŸ’»
samqbush
samqbush

πŸ’»
sdanzo-hrb
sdanzo-hrb

πŸ’»
voidfnc
voidfnc

πŸ’»
Wendy Breiding
Wendy Breiding

πŸ’»
Ankur Sharma
Ankur Sharma

πŸ’»
黃ε₯ζ—» Vincent Huang
黃ε₯ζ—» Vincent Huang

πŸ’»
μ΄μƒν˜„
μ΄μƒν˜„

πŸ’»
Abdi Daud
Abdi Daud

πŸ’»
Adrien Clerbois
Adrien Clerbois

πŸ’»
Alan Sprecacenere
Alan Sprecacenere

πŸ’»
AndrΓ© Silva
AndrΓ© Silva

πŸ’»
Antoine Rey
Antoine Rey

πŸ’»
Artem Saveliev
Artem Saveliev

πŸ’»
Bruno Borges
Bruno Borges

πŸ’»
Christophe Peugnet
Christophe Peugnet

πŸ’»
Chtive
Chtive

πŸ’»
Craig Bekker
Craig Bekker

πŸ’»
Dan
Dan

πŸ’»
Eldrick Wega
Eldrick Wega

πŸ’»
Felix Arjuna
Felix Arjuna

πŸ’»
Furkan Enes
Furkan Enes

πŸ’»
Genevieve Warren
Genevieve Warren

πŸ’»
George Dernikos
George Dernikos

πŸ’»
Giovanni de Almeida Martins
Giovanni de Almeida Martins

πŸ’»
Ioana A
Ioana A

πŸ’»
Jakub JareΕ‘
Jakub JareΕ‘

πŸ’»
Joe Watkins
Joe Watkins

πŸ’»
John Papa
John Papa

πŸ’»
Joseph Gonzales
Joseph Gonzales

πŸ’»
JosΓ© Antonio Garrido
JosΓ© Antonio Garrido

πŸ’»
Kim Skov Rasmussen
Kim Skov Rasmussen

πŸ’»
Kenny White
Kenny White

πŸ’»
Louella Creemers
Louella Creemers

πŸ’»
Luke Murray
Luke Murray

πŸ’»
Mark Noble
Mark Noble

πŸ’»
Add your contributions
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! ## πŸ“š Additional Resources - [VS Code Copilot Customization Documentation](https://code.visualstudio.com/docs/copilot/copilot-customization) - Official Microsoft documentation - [GitHub Copilot Chat Documentation](https://code.visualstudio.com/docs/copilot/chat/copilot-chat) - Complete chat feature guide - [Custom Chat Modes](https://code.visualstudio.com/docs/copilot/chat/chat-modes) - Advanced chat configuration - [VS Code Settings](https://code.visualstudio.com/docs/getstarted/settings) - General VS Code configuration guide ## πŸ› οΈ Development Configuration This repository uses various configuration files to ensure consistent code style and avoid issues with line endings: - [\`.editorconfig\`](.editorconfig) - Defines coding styles across different editors and IDEs - [\`.gitattributes\`](.gitattributes) - Ensures consistent line endings in text files - [\`.vscode/settings.json\`](.vscode/settings.json) - VS Code-specific settings for this repository - [\`.vscode/extensions.json\`](.vscode/extensions.json) - Recommended VS Code extensions > πŸ’‘ **Note**: All markdown files in this repository use LF line endings (Unix-style) to avoid mixed line endings issues. The repository is configured to automatically handle line endings conversion. ## πŸ“„ License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## 🀝 Code of Conduct Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. ## ℒ️ Trademarks This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.`, }; // Add error handling utility function safeFileOperation(operation, filePath, defaultValue = null) { try { return operation(); } catch (error) { console.error(`Error processing file ${filePath}: ${error.message}`); return defaultValue; } } function extractTitle(filePath) { return safeFileOperation( () => { const content = fs.readFileSync(filePath, "utf8"); const lines = content.split("\n"); // Step 1: Look for title in frontmatter for all file types let inFrontmatter = false; let frontmatterEnded = false; for (const line of lines) { if (line.trim() === "---") { if (!inFrontmatter) { inFrontmatter = true; } else if (!frontmatterEnded) { frontmatterEnded = true; } continue; } if (inFrontmatter && !frontmatterEnded) { // Look for title field in frontmatter if (line.includes("title:")) { // Extract everything after 'title:' const afterTitle = line .substring(line.indexOf("title:") + 6) .trim(); // Remove quotes if present const cleanTitle = afterTitle.replace(/^['"]|['"]$/g, ""); return cleanTitle; } } } // Reset for second pass inFrontmatter = false; frontmatterEnded = false; // Step 2: For prompt/chatmode/instructions files, look for heading after frontmatter if ( filePath.includes(".prompt.md") || filePath.includes(".chatmode.md") || filePath.includes(".instructions.md") ) { for (const line of lines) { if (line.trim() === "---") { if (!inFrontmatter) { inFrontmatter = true; } else if (inFrontmatter && !frontmatterEnded) { frontmatterEnded = true; } continue; } if (frontmatterEnded && line.startsWith("# ")) { return line.substring(2).trim(); } } // Step 3: Format filename for prompt/chatmode/instructions files if no heading found const basename = path.basename( filePath, filePath.includes(".prompt.md") ? ".prompt.md" : filePath.includes(".chatmode.md") ? ".chatmode.md" : ".instructions.md" ); return basename .replace(/[-_]/g, " ") .replace(/\b\w/g, (l) => l.toUpperCase()); } // Step 4: For instruction files, look for the first heading for (const line of lines) { if (line.startsWith("# ")) { return line.substring(2).trim(); } } // Step 5: Fallback to filename const basename = path.basename(filePath, path.extname(filePath)); return basename .replace(/[-_]/g, " ") .replace(/\b\w/g, (l) => l.toUpperCase()); }, filePath, path .basename(filePath, path.extname(filePath)) .replace(/[-_]/g, " ") .replace(/\b\w/g, (l) => l.toUpperCase()) ); } function extractDescription(filePath) { return safeFileOperation( () => { const content = fs.readFileSync(filePath, "utf8"); // Parse frontmatter for description (for both prompts and instructions) const lines = content.split("\n"); let inFrontmatter = false; // For multi-line descriptions let isMultilineDescription = false; let multilineDescription = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.trim() === "---") { if (!inFrontmatter) { inFrontmatter = true; continue; } break; } if (inFrontmatter) { // Check for multi-line description with pipe syntax (|) const multilineMatch = line.match(/^description:\s*\|(\s*)$/); if (multilineMatch) { isMultilineDescription = true; // Continue to next line to start collecting the multi-line content continue; } // If we're collecting a multi-line description if (isMultilineDescription) { // If the line has no indentation or has another frontmatter key, stop collecting if (!line.startsWith(" ") || line.match(/^[a-zA-Z0-9_-]+:/)) { // Join the collected lines and return return multilineDescription.join(" ").trim(); } // Add the line to our multi-line collection (removing the 2-space indentation) multilineDescription.push(line.substring(2)); } else { // Look for single-line description field in frontmatter const descriptionMatch = line.match( /^description:\s*['"]?(.+?)['"]?\s*$/ ); if (descriptionMatch) { let description = descriptionMatch[1]; // Check if the description is wrapped in single quotes and handle escaped quotes const singleQuoteMatch = line.match(/^description:\s*'(.+?)'\s*$/); if (singleQuoteMatch) { // Replace escaped single quotes ('') with single quotes (') description = singleQuoteMatch[1].replace(/''/g, "'"); } return description; } } } } // If we've collected multi-line description but the frontmatter ended if (multilineDescription.length > 0) { return multilineDescription.join(" ").trim(); } return null; }, filePath, null ); } /** * Generate badges for installation links in VS Code and VS Code Insiders. * @param {string} link - The relative link to the instructions or prompts file. * @returns {string} - Markdown formatted badges for installation. */ const vscodeInstallImage = "https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white"; const vscodeInsidersInstallImage = "https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white"; const repoBaseUrl = "https://raw.githubusercontent.com/github/awesome-copilot/main"; const vscodeBaseUrl = "https://vscode.dev/redirect?url="; const vscodeInsidersBaseUrl = "https://insiders.vscode.dev/redirect?url="; function makeBadges(link, type) { return `[![Install in VS Code](${vscodeInstallImage})](${vscodeBaseUrl}${encodeURIComponent( `vscode:chat-${type}/install?url=${repoBaseUrl}/${link})` )}
[![Install in VS Code](${vscodeInsidersInstallImage})](${vscodeInsidersBaseUrl}${encodeURIComponent( `vscode-insiders:chat-${type}/install?url=${repoBaseUrl}/${link})` )}`; } /** * Generate the instructions section with a table of all instructions */ function generateInstructionsSection(instructionsDir) { // Check if directory exists if (!fs.existsSync(instructionsDir)) { return ""; } // Get all instruction files const instructionFiles = fs .readdirSync(instructionsDir) .filter((file) => file.endsWith(".md")) .sort(); console.log(`Found ${instructionFiles.length} instruction files`); // Return empty string if no files found if (instructionFiles.length === 0) { return ""; } // Create table header let instructionsContent = "| Title | Description |\n| ----- | ----------- |\n"; // Generate table rows for each instruction file for (const file of instructionFiles) { const filePath = path.join(instructionsDir, file); const title = extractTitle(filePath); const link = encodeURI(`instructions/${file}`); // Check if there's a description in the frontmatter const customDescription = extractDescription(filePath); // Create badges for installation links const badges = makeBadges(link, "instructions"); if (customDescription && customDescription !== "null") { // Use the description from frontmatter instructionsContent += `| [${title}](${link})
${badges} | ${customDescription} |\n`; } else { // Fallback to the default approach - use last word of title for description, removing trailing 's' if present const topic = title.split(" ").pop().replace(/s$/, ""); instructionsContent += `| [${title}](${link})
${badges} | ${topic} specific coding standards and best practices |\n`; } } return `${TEMPLATES.instructionsSection}\n${TEMPLATES.instructionsUsage}\n\n${instructionsContent}`; } /** * Generate the prompts section with a table of all prompts */ function generatePromptsSection(promptsDir) { // Check if directory exists if (!fs.existsSync(promptsDir)) { return ""; } // Get all prompt files const promptFiles = fs .readdirSync(promptsDir) .filter((file) => file.endsWith(".prompt.md")) .sort(); console.log(`Found ${promptFiles.length} prompt files`); // Return empty string if no files found if (promptFiles.length === 0) { return ""; } // Create table header let promptsContent = "| Title | Description |\n| ----- | ----------- |\n"; // Generate table rows for each prompt file for (const file of promptFiles) { const filePath = path.join(promptsDir, file); const title = extractTitle(filePath); const link = encodeURI(`prompts/${file}`); // Check if there's a description in the frontmatter const customDescription = extractDescription(filePath); // Create badges for installation links const badges = makeBadges(link, "prompt"); if (customDescription && customDescription !== "null") { promptsContent += `| [${title}](${link})
${badges} | ${customDescription} |\n`; } else { promptsContent += `| [${title}](${link})
${badges} | | |\n`; } } return `${TEMPLATES.promptsSection}\n${TEMPLATES.promptsUsage}\n\n${promptsContent}`; } /** * Generate the chat modes section with a table of all chat modes */ function generateChatModesSection(chatmodesDir) { // Check if chatmodes directory exists if (!fs.existsSync(chatmodesDir)) { console.log("Chat modes directory does not exist"); return ""; } // Get all chat mode files const chatmodeFiles = fs .readdirSync(chatmodesDir) .filter((file) => file.endsWith(".chatmode.md")) .sort(); console.log(`Found ${chatmodeFiles.length} chat mode files`); // If no chat modes, return empty string if (chatmodeFiles.length === 0) { return ""; } // Create table header let chatmodesContent = "| Title | Description |\n| ----- | ----------- |\n"; // Generate table rows for each chat mode file for (const file of chatmodeFiles) { const filePath = path.join(chatmodesDir, file); const title = extractTitle(filePath); const link = encodeURI(`chatmodes/${file}`); // Check if there's a description in the frontmatter const customDescription = extractDescription(filePath); // Create badges for installation links const badges = makeBadges(link, "mode"); if (customDescription && customDescription !== "null") { chatmodesContent += `| [${title}](${link})
${badges} | ${customDescription} |\n`; } else { chatmodesContent += `| [${title}](${link})
${badges} | | |\n`; } } return `${TEMPLATES.chatmodesSection}\n${TEMPLATES.chatmodesUsage}\n\n${chatmodesContent}`; } /** * Generate the complete README.md content from scratch */ function generateReadme() { const instructionsDir = path.join(__dirname, "instructions"); const promptsDir = path.join(__dirname, "prompts"); const chatmodesDir = path.join(__dirname, "chatmodes"); // Generate each section const instructionsSection = generateInstructionsSection(instructionsDir); const promptsSection = generatePromptsSection(promptsDir); const chatmodesSection = generateChatModesSection(chatmodesDir); // Build the complete README content with template sections const sections = [TEMPLATES.header]; // Only include sections that have content if (instructionsSection.trim()) sections.push(instructionsSection); if (promptsSection.trim()) sections.push(promptsSection); if (chatmodesSection.trim()) sections.push(chatmodesSection); sections.push(TEMPLATES.footer); return sections.join("\n\n"); } // Main execution try { console.log("Generating README.md from scratch..."); const readmePath = path.join(__dirname, "README.md"); const newReadmeContent = generateReadme(); // Check if the README file already exists if (fs.existsSync(readmePath)) { const originalContent = fs.readFileSync(readmePath, "utf8"); const hasChanges = originalContent !== newReadmeContent; if (hasChanges) { fs.writeFileSync(readmePath, newReadmeContent); console.log("README.md updated successfully!"); } else { console.log("README.md is already up to date. No changes needed."); } } else { // Create the README file if it doesn't exist fs.writeFileSync(readmePath, newReadmeContent); console.log("README.md created successfully!"); } } catch (error) { console.error(`Error generating README.md: ${error.message}`); process.exit(1); }