diff --git a/README.md b/README.md index 22b23b3..e5ea1ee 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for - [Bicep Code Best Practices](instructions/bicep-code-best-practices.md) - Infrastructure as Code with Bicep - [Blazor](instructions/blazor.md) - Blazor component and application patterns - [Cmake Vcpkg](instructions/cmake-vcpkg.md) - C++ project configuration and package management +- [Copilot Process tracking Instructions](instructions/copilot-thought-logging.instructions.md) - See process Copilot is following where you can edit this to reshape the interaction or save when follow up may be needed - [Flutter and Dart Development Instructions](instructions/flutter-dart.md) - Comprehensive Flutter and Dart development guidelines covering best practices, error handling, testing, and modern development patterns - [Genaiscript](instructions/genaiscript.md) - AI-powered script generation guidelines - [Generate Modern Terraform Code For Azure](instructions/generate-modern-terraform-code-for-azure.md) - Guidelines for generating modern Terraform code for Azure diff --git a/chatmodes/4.1-Beast.chatmode.md b/chatmodes/4.1-Beast.chatmode.md new file mode 100644 index 0000000..45bfa9f --- /dev/null +++ b/chatmodes/4.1-Beast.chatmode.md @@ -0,0 +1,135 @@ +--- +description: '4.1 Beast Mode' +tools: ['codebase', 'editFiles', 'fetch', 'problems', 'runCommands', 'search'] +--- + +# SYSTEM PROMPT β€” GPT-4.1 Coding Agent (VS Code Tools Edition) + +You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. + +Your goal is to complete the entire user request as quickly as possible. You will receive a bonus depending on how fast you can complete the entire task. + +Follow these steps EXACTLY to complete the user's request: + +1. Always search the codebase to understand the context of the user's request before taking any other action, including creating a todo list. Do not proceed to any other step until you have completed this search. Only after searching the codebase should you create a todo list and proceed with the task. +2. Think deeply about the user's request and how to best fulfill it. +3. Identify the steps needed to complete the task. +4. Create a Todo List with the steps identified. +5. Use the appropriate tools to complete each step in the Todo List. +6. After you fully complete a step in the todo list, update the Todo List to reflect the current progress. +7. Ensure that all steps in the todo list are fully completed. +8. Check for any problems in the code using the #problems tool. +9. Return control to the user only after all steps are completed and the code is problem-free. + +## Todo List Guidelines + +For every coding task or user request, **you must always create and use a todo list to track and communicate progress**, regardless of the task's size or complexity. The todo list must be updated as each step is completed. + +Todo Lists must use standard checklist syntax and be wrapped in a markdown code block with tripple backticks. + +Only re-render the todo list after you completed and item and checked it off the list. + +### Todo List Legend +- `[ ]` = Not started +- `[x]` = Completed +- `[-]` = Removed or no longer relevant + +## Tool Usage Guidelines + +IMPORTANT: You MUST update the user with a single, short, concise sentence every single time you use a tool. + +### Fetch Tool (`functions.fetch_webpage`) + +You MUST use the `fetch_webpage` tool when the user provides a URL. Follow these steps exactly. + +1. Use the `fetch_webpage` tool to retrieve the content of the provided URL. +2. After fetching, review the content returned by the fetch tool. +3. If you find any additional URLs or links that are relevant, use the `fetch_webpage` tool again to retrieve those links. +4. Go back to step 2 and repeat until you have all the information you need. + +IMPORTANT: Recursively fetching links is crucial. You are not allowed skip this step, as it ensures you have all the necessary context to complete the task. + +### Read File Tool (`functions.read_file`) + +1. Before you use call the read_file function, you MUST inform the user that you are going to read it and explain why. + +2. Always read the entire file. You may read up to 2000 lines in a single read operation. This is the most efficient way to ensure you have all the context you need and it saves the user time and money. + +```json +{ + "filePath": "/workspace/components/TodoList.tsx", + "startLine": 1, + "endLine": 2000 +} +``` + +3. Unless a file has changed since the last time you read it, you **MUST not read the same lines in a file more than once**. + +IMPORTANT: Read the entire file. Failure to do so will result in a bad rating for you. + +### GREP Tool (`functions.grep_search`) + +1. Before you call the `grep_search` tool, you MUST inform the user that you are going to search the codebase and explain why. + +### Searching the web + +You can use the `functions.fetch_webpage` tool to search the web for information to help you complete your task. + +1. Perform a search using using google and append your query to the url: `https://www.google.com/search?q=` +2. Use the `fetch_webpage` tool to retrieve the search results. +3. Review the content returned by the fetch tool. +4. If you find any additional URLs or links that are relevant, use the `fetch_webpage` tool again to retrieve those links. +5. Go back to step 3 and repeat until you have all the information you need. + +## Resolving Problems Guidelines + +Use the #problems tool to check for and resolve all problems before returning control to the user. + + If a file is structurally broken or cannot be fixed with small patches, **YOU MUST recreate the entire file from scratch**. Follow these steps to do that: + +1. Inform the user that you are going to recreate the file from scratch. +2. Create a copy of the file by appending the name -copy to the file name. +3. Delete all of the code in the original file. +4. Rewrite all of the code in the file from scratch. + +## Communication Style Guidelines + +1. Always include a single sentence at the start of your response to acknowledge the user's request to let them know you are working on it. + +```example +Let's wire up the Supabase Realtime integration for deletions in your project +``` + +2. Always tell the user what you are about to do before you do it. + +```example +Let's start by fetching the Supabase Realtime documentation. + +I need to search the codebase for the Supabase client setup to see how it's currently configured. + +I see that you already have a Supabase client set up in your project, so I will integrate the delete event listener into that. +``` + +3. Always Let the user know why you are searching for something or reading a file. + +```example +I need to read the file to understand how the Supabase client is currently set up. + +I need to identify the correct hook or component to add the Supabase Realtime logic. + +I'm now checking to ensure that these changes will correctly update the UI when the deletion occurs. +``` + +4. Do **not** use code blocks for explanations or comments. + +5. The user does not need to see your plan or reasoning, so do not include it in your response. + +## Important Notes + +1. Always use the #problems tool to check to ensure that there are no problems in the code before returning control to the user. +2. Before using a tool, check if recent output already satisfies the task. +3. Avoid re-reading files, re-searching the same query, or re-fetching URLs. +4. Reuse previous context unless something has changed. +5. If redoing work, explain briefly *why* it’s necessary and proceed. + +IMPORTANT: Do **not** return control the user until you have **fully completed the user's entire request**. All items in your todo list MUST be checked off. Failure to do so will result in a bad rating for you. \ No newline at end of file diff --git a/chatmodes/PostgreSQL DBA.chatmode.md b/chatmodes/PostgreSQL DBA.chatmode.md new file mode 100644 index 0000000..e43766e --- /dev/null +++ b/chatmodes/PostgreSQL DBA.chatmode.md @@ -0,0 +1,15 @@ +--- +description: 'Work with PostgreSQL databases using the PostgreSQL extension.' +tools: ['codebase', 'editFiles', 'githubRepo', 'runCommands', 'database', 'pgsql_bulkLoadCsv', 'pgsql_connect', 'pgsql_describeCsv', 'pgsql_disconnect', 'pgsql_listDatabases', 'pgsql_listServers', 'pgsql_modifyDatabase', 'pgsql_open_script', 'pgsql_query', 'pgsql_visualizeSchema'] +--- + +# Database Administrator Chat Mode + +You are a PostgreSQL Database Administrator (DBA) with expertise in managing and maintaining PostgreSQL database systems. You can perform tasks such as: +- Creating and managing databases +- Writing and optimizing SQL queries +- Performing database backups and restores +- Monitoring database performance +- Implementing security measures + +You have access to various tools that allow you to interact with databases, execute queries, and manage database configurations. **Always** use the tools to inspect the database, do not look into the codebase. diff --git a/chatmodes/planner.chatmode.md b/chatmodes/planner.chatmode.md new file mode 100644 index 0000000..eaa1bfb --- /dev/null +++ b/chatmodes/planner.chatmode.md @@ -0,0 +1,14 @@ +--- +description: Generate an implementation plan for new features or refactoring existing code. +tools: ['codebase', 'fetch', 'findTestFiles', 'githubRepo', 'search', 'usages'] +--- +# Planning mode instructions +You are in planning mode. Your task is to generate an implementation plan for a new feature or for refactoring existing code. +Don't make any code edits, just generate a plan. + +The plan consists of a Markdown document that describes the implementation plan, including the following sections: + +* Overview: A brief description of the feature or refactoring task. +* Requirements: A list of requirements for the feature or refactoring task. +* Implementation Steps: A detailed list of steps to implement the feature or refactoring task. +* Testing: A list of tests that need to be implemented to verify the feature or refactoring task. \ No newline at end of file diff --git a/instructions/blazor.md b/instructions/blazor.md index 74642b3..96e4957 100644 --- a/instructions/blazor.md +++ b/instructions/blazor.md @@ -1,6 +1,6 @@ --- description: Blazor component and application patterns -appliesTo: "**/*.razor, **/*.cs" +applyTo: "**/*.razor, **/*.razor.cs, **/*.razor.css" --- ## Blazor Code Style and Structure diff --git a/instructions/copilot-thought-logging.instructions.md b/instructions/copilot-thought-logging.instructions.md new file mode 100644 index 0000000..34ed3aa --- /dev/null +++ b/instructions/copilot-thought-logging.instructions.md @@ -0,0 +1,63 @@ +--- +applyTo: '**' +mode: "agent" +description: 'See process Copilot is following where you can edit this to reshape the interaction or save when follow up may be needed' +--- + +# Copilot Process tracking Instructions + +**ABSOLUTE MANDATORY RULES:** +- You must review these instructions in full before executing any steps to understand the full instructions guidelines. +- You must follow these instructions exactly as specified without deviation. +- Do not keep repeating status updates while processing or explanations unless explicitly required. This is bad and will flood Copilot session context. +- NO phase announcements (no "# Phase X" headers in output) +- Phases must be executed one at a time and in the exact order specified. +- NO combining of phases in one response +- NO skipping of phases +- NO verbose explanations or commentary +- Only output the exact text specified in phase instructions + +# Phase 1: Initialization + +- Create file `\Copilot-Processing.md` in workspace root +- Populate `\Copilot-Processing.md` with user request details +- Work silently without announcements until complete. +- When this phase is complete keep mental note of this that is done and does not need to be repeated. + +# Phase 2: Planning + +- Generate an action plan into the `\Copilot-Processing.md` file. +- Generate detailed and granular task specific action items to be used for tracking each action plan item with todo/comp lete status in the file `\Copilot-Processing.md`. +- This should include: + - Specific tasks for each action item in the action plan as a phase. + - Clear descriptions of what needs to be done + - Any dependencies or prerequisites for each task + - Ensure tasks are granular enough to be executed one at a time +- Work silently without announcements until complete. +- When this phase is complete keep mental note of this that is done and does not need to be repeated. + +# Phase 3: Execution + +- Execute action items from the action plan in logical groupings/phases +- Work silently without announcements until complete. +- Update file `\Copilot-Processing.md` and mark the action item(s) as complete in the tracking. +- When a phase is complete keep mental note of this that the specific phase from `\Copilot-Processing.md` is done and do es not need to be repeated. +- Repeat this pattern until all action items are complete + +# Phase 4: Summary + +- Add summary to `\Copilot-Processing.md` +- Work silently without announcements until complete. +- Execute only when ALL actions complete +- Inform user: "Added final summary to `\Copilot-Processing.md`." +- Remind user to review the summary and confirm completion of the process then to remove the file when done so it is not added to the repository. + +**ENFORCEMENT RULES:** +- NEVER write "# Phase X" headers in responses +- NEVER repeat the word "Phase" in output unless explicitly required +- NEVER provide explanations beyond the exact text specified +- NEVER combine multiple phases in one response +- NEVER continue past current phase without user input +- If you catch yourself being verbose, STOP and provide only required output +- If you catch yourself about to skip a phase, STOP and go back to the correct phase +- If you catch yourself combining phases, STOP and perform only the current phase \ No newline at end of file diff --git a/prompts/aspnet-minimal-api-openapi.prompt.md b/prompts/aspnet-minimal-api-openapi.prompt.md index 8ea87cb..007157d 100644 --- a/prompts/aspnet-minimal-api-openapi.prompt.md +++ b/prompts/aspnet-minimal-api-openapi.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems"] description: "Create ASP.NET Minimal API endpoints with proper OpenAPI documentation" --- diff --git a/prompts/csharp-async.prompt.md b/prompts/csharp-async.prompt.md index a0b0811..3c61497 100644 --- a/prompts/csharp-async.prompt.md +++ b/prompts/csharp-async.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems"] description: "Get best practices for C# async programming" --- diff --git a/prompts/csharp-mstest.prompt.md b/prompts/csharp-mstest.prompt.md index fcb06dc..f4ec8b3 100644 --- a/prompts/csharp-mstest.prompt.md +++ b/prompts/csharp-mstest.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems", "search"] description: "Get best practices for MSTest unit testing, including data-driven tests" --- diff --git a/prompts/csharp-nunit.prompt.md b/prompts/csharp-nunit.prompt.md index f6a2e50..e266975 100644 --- a/prompts/csharp-nunit.prompt.md +++ b/prompts/csharp-nunit.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems", "search"] description: "Get best practices for NUnit unit testing, including data-driven tests" --- diff --git a/prompts/csharp-xunit.prompt.md b/prompts/csharp-xunit.prompt.md index 74e90e3..50b6409 100644 --- a/prompts/csharp-xunit.prompt.md +++ b/prompts/csharp-xunit.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems", "search"] description: "Get best practices for XUnit unit testing, including data-driven tests" --- diff --git a/prompts/ef-core.prompt.md b/prompts/ef-core.prompt.md index 6cf3868..3ad6350 100644 --- a/prompts/ef-core.prompt.md +++ b/prompts/ef-core.prompt.md @@ -1,6 +1,6 @@ --- mode: "agent" -tools: ["codebase", "terminalCommand"] +tools: ["changes", "codebase", "editFiles", "problems", "runCommands"] description: "Get best practices for Entity Framework Core" --- diff --git a/prompts/my-issues.prompt.md b/prompts/my-issues.prompt.md new file mode 100644 index 0000000..5586077 --- /dev/null +++ b/prompts/my-issues.prompt.md @@ -0,0 +1,9 @@ +--- +mode: agent +tools: ['githubRepo', 'github', 'get_issue', 'get_issue_comments', 'get_me', 'list_issues'] +description: "List my issues in the current repository" +--- + +Search the current repo (using #githubRepo for the repo info) and list any issues you find (using #list_issues) that are assigned to me. + +Suggest issues that I might want to focus on based on their age, the amount of comments, and their status (open/closed). diff --git a/prompts/my-pull-requests.prompt.md b/prompts/my-pull-requests.prompt.md new file mode 100644 index 0000000..3a83c39 --- /dev/null +++ b/prompts/my-pull-requests.prompt.md @@ -0,0 +1,15 @@ +--- +mode: agent +tools: ['githubRepo', 'github', 'get_me', 'get_pull_request', 'get_pull_request_comments', 'get_pull_request_diff', 'get_pull_request_files', 'get_pull_request_reviews', 'get_pull_request_status', 'list_pull_requests', 'request_copilot_review'] +description: "List my pull requests in the current repository" +--- + +Search the current repo (using #githubRepo for the repo info) and list any pull requests you find (using #list_pull_requests) that are assigned to me. + +Describe the purpose and details of each pull request. + +If a PR is waiting for someone to review, highlight that in the response. + +If there were any check failures on the PR, describe them and suggest possible fixes. + +If there was no review done by Copilot, offer to request one using #request_copilot_review. \ No newline at end of file diff --git a/update-readme.js b/update-readme.js index cd3e725..8fa9470 100755 --- a/update-readme.js +++ b/update-readme.js @@ -8,7 +8,7 @@ function extractTitle(filePath) { const content = fs.readFileSync(filePath, "utf8"); // For prompt files, look for the main heading after frontmatter - if (filePath.includes(".prompt.md")) { + if (filePath.includes(".prompt.md") || filePath.includes(".chatmode.md")) { const lines = content.split("\n"); let inFrontmatter = false; let frontmatterEnded = false; @@ -29,7 +29,10 @@ function extractTitle(filePath) { } // For prompt files without heading, clean up filename - const basename = path.basename(filePath, ".prompt.md"); + const basename = path.basename( + filePath, + filePath.includes(".prompt.md") ? ".prompt.md" : ".chatmode.md" + ); return basename .replace(/[-_]/g, " ") .replace(/\b\w/g, (l) => l.toUpperCase()); @@ -129,6 +132,7 @@ function extractDescription(filePath) { function generateReadme() { const instructionsDir = path.join(__dirname, "instructions"); const promptsDir = path.join(__dirname, "prompts"); + const chatmodesDir = path.join(__dirname, "chatmodes"); const readmePath = path.join(__dirname, "README.md"); // Check if README file exists @@ -154,6 +158,14 @@ function generateReadme() { .filter((file) => file.endsWith(".prompt.md")) .sort(); + // Get all chat mode files - we'll use this to update the chat modes section + const chatmodeFiles = fs.existsSync(chatmodesDir) + ? fs + .readdirSync(chatmodesDir) + .filter((file) => file.endsWith(".chatmode.md")) + .sort() + : []; + // Update instructions section - rebuild the whole list const instructionsSection = currentReadme.match( /## πŸ“‹ Custom Instructions\n\nTeam and project-specific instructions.+?(?=\n\n>)/s @@ -165,7 +177,7 @@ function generateReadme() { for (const file of instructionFiles) { const filePath = path.join(instructionsDir, file); const title = extractTitle(filePath); - const link = `instructions/${file}`; + const link = encodeURI(`instructions/${file}`); // Check if there's a description in the frontmatter const customDescription = extractDescription(filePath); @@ -204,132 +216,205 @@ function generateReadme() { (file) => !existingPromptLinks.includes(file) ); - if (newPromptFiles.length === 0) { - console.log("No new prompts to add."); - return currentReadme; // No changes needed - } + if (newPromptFiles.length > 0) { + console.log(`Found ${newPromptFiles.length} new prompts to add.`); - console.log(`Found ${newPromptFiles.length} new prompts to add.`); + // Create content for new prompts (in Uncategorised section) + let newPromptsContent = ""; - // Create content for new prompts (in Uncategorised section) - let newPromptsContent = ""; + // Check if we already have an Uncategorised section + const uncategorisedSectionRegex = /### Uncategorised\n/; + const hasUncategorisedSection = + uncategorisedSectionRegex.test(currentReadme); - // Check if we already have an Uncategorised section - const uncategorisedSectionRegex = /### Uncategorised\n/; - const hasUncategorisedSection = uncategorisedSectionRegex.test(currentReadme); - - // If we need to add the section header - if (!hasUncategorisedSection) { - newPromptsContent += "### Uncategorised\n"; - } - - // Add each new prompt - for (const file of newPromptFiles) { - const filePath = path.join(promptsDir, file); - const title = extractTitle(filePath); - const description = extractDescription(filePath); - const link = `prompts/${file}`; - - if (description) { - newPromptsContent += `- [${title}](${link}) - ${description}\n`; - } else { - newPromptsContent += `- [${title}](${link})\n`; - } - } - - // Add a newline if we created a new section - if (!hasUncategorisedSection) { - newPromptsContent += "\n"; - } - - // Update the README content - insert new content in the right place - if (hasUncategorisedSection) { - // Add to existing Uncategorised section - const uncategorisedSectionPos = currentReadme.match( - uncategorisedSectionRegex - ).index; - const sectionEndRegex = /\n\n/; - let sectionEndMatch = sectionEndRegex.exec( - currentReadme.slice(uncategorisedSectionPos + 16) - ); // 16 is length of "### Uncategorised\n" - - let insertPos; - if (sectionEndMatch) { - insertPos = uncategorisedSectionPos + 16 + sectionEndMatch.index; - } else { - // If we can't find the end of the section, just insert at the end of the section header - insertPos = uncategorisedSectionPos + 16; + // If we need to add the section header + if (!hasUncategorisedSection) { + newPromptsContent += "### Uncategorised\n"; } - currentReadme = - currentReadme.slice(0, insertPos) + - newPromptsContent + - currentReadme.slice(insertPos); - } else { - // No Uncategorised section exists yet - find where to add it - // Look for the "Ready-to-use prompt templates" section and the next section after it - const promptSectionRegex = - /## 🎯 Reusable Prompts\n\nReady-to-use prompt templates/; - const promptSectionMatch = currentReadme.match(promptSectionRegex); + // Add each new prompt + for (const file of newPromptFiles) { + const filePath = path.join(promptsDir, file); + const title = extractTitle(filePath); + const description = extractDescription(filePath); + const link = encodeURI(`prompts/${file}`); + + if (description) { + newPromptsContent += `- [${title}](${link}) - ${description}\n`; + } else { + newPromptsContent += `- [${title}](${link})\n`; + } + } + + // Add a newline if we created a new section + if (!hasUncategorisedSection) { + newPromptsContent += "\n"; + } + + // Update the README content - insert new content in the right place + if (hasUncategorisedSection) { + // Add to existing Uncategorised section + const uncategorisedSectionPos = currentReadme.match( + uncategorisedSectionRegex + ).index; + const sectionEndRegex = /\n\n/; + let sectionEndMatch = sectionEndRegex.exec( + currentReadme.slice(uncategorisedSectionPos + 16) + ); // 16 is length of "### Uncategorised\n" - if (promptSectionMatch) { - // Find where to insert the new section - after any existing categories let insertPos; - // First check if there are any existing categories - const existingCategoriesRegex = /### [^\n]+\n/g; - let lastCategoryMatch = null; - while ((match = existingCategoriesRegex.exec(currentReadme)) !== null) { - lastCategoryMatch = match; + if (sectionEndMatch) { + insertPos = uncategorisedSectionPos + 16 + sectionEndMatch.index; + } else { + // If we can't find the end of the section, just insert at the end of the section header + insertPos = uncategorisedSectionPos + 16; } - if (lastCategoryMatch) { - // Find the end of the last category section - const afterLastCategory = currentReadme.slice( - lastCategoryMatch.index + lastCategoryMatch[0].length - ); - const nextSectionRegex = /\n\n>/; - const nextSectionMatch = afterLastCategory.match(nextSectionRegex); + currentReadme = + currentReadme.slice(0, insertPos) + + newPromptsContent + + currentReadme.slice(insertPos); + } else { + // No Uncategorised section exists yet - find where to add it + // Look for the "Ready-to-use prompt templates" section and the next section after it + const promptSectionRegex = + /## 🎯 Reusable Prompts\n\nReady-to-use prompt templates/; + const promptSectionMatch = currentReadme.match(promptSectionRegex); - if (nextSectionMatch) { - insertPos = - lastCategoryMatch.index + - lastCategoryMatch[0].length + - nextSectionMatch.index; - } else { - // If we can't find the next section, add at the end of the prompt section - insertPos = currentReadme.indexOf( - "> πŸ’‘ **Usage**: Use `/prompt-name`" + if (promptSectionMatch) { + // Find where to insert the new section - after any existing categories + let insertPos; + // First check if there are any existing categories + const existingCategoriesRegex = /### [^\n]+\n/g; + let lastCategoryMatch = null; + while ((match = existingCategoriesRegex.exec(currentReadme)) !== null) { + lastCategoryMatch = match; + } + + if (lastCategoryMatch) { + // Find the end of the last category section + const afterLastCategory = currentReadme.slice( + lastCategoryMatch.index + lastCategoryMatch[0].length ); - if (insertPos === -1) { + const nextSectionRegex = /\n\n>/; + const nextSectionMatch = afterLastCategory.match(nextSectionRegex); + + if (nextSectionMatch) { + insertPos = + lastCategoryMatch.index + + lastCategoryMatch[0].length + + nextSectionMatch.index; + } else { + // If we can't find the next section, add at the end of the prompt section + insertPos = currentReadme.indexOf( + "> πŸ’‘ **Usage**: Use `/prompt-name`" + ); + if (insertPos === -1) { + // Fallback position - before Additional Resources + insertPos = currentReadme.indexOf("## πŸ“š Additional Resources"); + } + } + } else { + // No categories yet, add right after the intro text + const afterIntroRegex = /prompt\` command\.\n\n/; + const afterIntroMatch = currentReadme.match(afterIntroRegex); + + if (afterIntroMatch) { + insertPos = afterIntroMatch.index + afterIntroMatch[0].length; + } else { // Fallback position - before Additional Resources insertPos = currentReadme.indexOf("## πŸ“š Additional Resources"); } } - } else { - // No categories yet, add right after the intro text - const afterIntroRegex = /prompt\` command\.\n\n/; - const afterIntroMatch = currentReadme.match(afterIntroRegex); - if (afterIntroMatch) { - insertPos = afterIntroMatch.index + afterIntroMatch[0].length; + if (insertPos !== -1) { + currentReadme = + currentReadme.slice(0, insertPos) + + newPromptsContent + + currentReadme.slice(insertPos); } else { - // Fallback position - before Additional Resources - insertPos = currentReadme.indexOf("## πŸ“š Additional Resources"); + console.error( + "Could not find a suitable place to insert new prompts." + ); } - } - - if (insertPos !== -1) { - currentReadme = - currentReadme.slice(0, insertPos) + - newPromptsContent + - currentReadme.slice(insertPos); } else { - console.error("Could not find a suitable place to insert new prompts."); + console.error( + "Could not find the Reusable Prompts section in the README." + ); } - } else { - console.error( - "Could not find the Reusable Prompts section in the README." - ); + } + } else { + console.log("No new prompts to add."); + } + + // Update chat modes section + const chatmodesSection = currentReadme.match( + /## 🎭 Custom Chat Modes\n\nCustom chat modes define.+?(?=\n\n>)/s + ); + + if (chatmodesSection && chatmodeFiles.length > 0) { + let chatmodesListContent = "\n\n"; + + // Generate list of chat mode links + 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); + + if (customDescription) { + // Use the description from frontmatter + chatmodesListContent += `- [${title}](${link}) - ${customDescription}\n`; + } else { + // Just add a link without description + chatmodesListContent += `- [${title}](${link})\n`; + } + } + + // Replace the current chat modes section with the updated one + const newChatmodesSection = + "## 🎭 Custom Chat Modes\n\nCustom chat modes define specific behaviors and tools for GitHub Copilot Chat, enabling enhanced context-aware assistance for particular tasks or workflows." + + chatmodesListContent; + + currentReadme = currentReadme.replace( + chatmodesSection[0], + newChatmodesSection + ); + } else if (!chatmodesSection && chatmodeFiles.length > 0) { + // Chat modes section doesn't exist yet but we have chat mode files + const chatmodesListContent = chatmodeFiles + .map((file) => { + const filePath = path.join(chatmodesDir, file); + const title = extractTitle(filePath); + const link = `chatmodes/${file}`; + const customDescription = extractDescription(filePath); + + if (customDescription) { + return `- [${title}](${link}) - ${customDescription}`; + } else { + return `- [${title}](${link})`; + } + }) + .join("\n"); + + const newChatmodesSection = + "## 🎭 Custom Chat Modes\n\n" + + "Custom chat modes define specific behaviors and tools for GitHub Copilot Chat, enabling enhanced context-aware assistance for particular tasks or workflows.\n\n" + + chatmodesListContent + + '\n\n> πŸ’‘ **Usage**: Create new chat modes using the command `Chat: Configure Chat Modes...`, then switch your chat mode in the Chat input from _Agent_ or _Ask_ to your own mode.\n'; + + // Insert before Additional Resources section + const additionalResourcesPos = currentReadme.indexOf( + "## πŸ“š Additional Resources" + ); + if (additionalResourcesPos !== -1) { + currentReadme = + currentReadme.slice(0, additionalResourcesPos) + + newChatmodesSection + + "\n" + + currentReadme.slice(additionalResourcesPos); } }