diff --git a/README.md b/README.md index f9ee747..0f86595 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 thought logging](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 - [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 - [Markdown](instructions/markdown.md) - Documentation and content creation standards @@ -55,9 +56,21 @@ Ready-to-use prompt templates for specific development scenarios and tasks. Thes ### Documentation & Project Management - [Comment Code Generate Tutorial](prompts/comment-code-generate-a-tutorial.prompt.md) - Transform code into educational content - [Generate Specs as Issues](prompts/gen-specs-as-issues.prompt.md) - Convert requirements into GitHub issues +- [My Issues](prompts/my-issues.prompt.md) +- [My Pull Requests](prompts/my-pull-requests.prompt.md) + + > 💡 **Usage**: Use `/prompt-name` in VS Code chat or run `Chat: Run Prompt` command. Prompt files support variables like `${input:name}` for dynamic content. +## 🧩 Custom Chat Modes + +You can define your own chat modes for specific scenarios, such as planning a new feature or getting the AI to behave a certain way when in a particular mode. Custom chat modes are defined in Markdown files with the `.chatmode.md` suffix and can be stored in the `.github` folder of your workspace or placed in your user profile. They should up in the "Ask/Edit/Agent" dropdown in the chat view. + +- [Planning mode instructions](chatmodes/planner.chatmode.md) - Generate an implementation plan for new features or refactoring existing code. +- [4.1 Beast Mode](chatmodes/4.1-Beast.chatmode.md) - A custom prompt to get GPT 4.1 to behave like a top-notch coding agent. + +Use `Chat > Configure Chat Modes` from the Command Palette to add/edit custom modes. ## 📚 Additional Resources @@ -66,13 +79,6 @@ Ready-to-use prompt templates for specific development scenarios and tasks. Thes - [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 -## 🧩 Modes - -You can define your own chat modes for specific scenarios, such as planning a new feature or getting the AI to behave a certain way when in a particular mode. Custom chat modes are defined in Markdown files with the `.chatmode.md` suffix and can be stored in the `.github` folder of your workspace or placed in your user profile. They should up in the "Ask/Edit/Agent" dropdown in the chat view. - -- [4.1 Beast Mode](modes/4.1-Beast.chatmode.md) - A custom prompt to get GPT 4.1 to behave like a top-notch coding agent. - -Use `Chat > Configure Chat Modes` from the Command Palette to add/edit custom modes. ## 📄 License 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..e877b76 --- /dev/null +++ b/instructions/copilot-thought-logging.instructions.md @@ -0,0 +1,62 @@ +--- +applyTo: '**' +mode: "agent" +--- + +# 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/complete 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 does 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/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..83a12bb 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 @@ -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 = `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 = `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. These `.chatmode.md` files can be loaded by selecting "Chat with Custom Mode" from the Copilot menu.' + + 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. These `.chatmode.md` files can be loaded by selecting "Chat with Custom Mode" from the Copilot menu.\n\n' + + chatmodesListContent + + '\n\n> 💡 **Usage**: Create a `.chatmode.md` file in your workspace or repository, then select "Chat with Custom Mode" from the Copilot Chat menu and select your chat mode file.\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); } }