From 30c7e48ec7c717c7dac4030f069ea902b761b670 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:26:00 +0000 Subject: [PATCH 1/2] Initial plan From 61928bca427b0bdc80a8ba52ce6ad7627ad35ecc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:34:40 +0000 Subject: [PATCH 2/2] Implement YAML configuration system for toggling prompts, instructions, and chat modes Co-authored-by: AstroSteveo <34114851+AstroSteveo@users.noreply.github.com> --- .gitignore | 9 ++ .schemas/config.schema.yml | 71 ++++++++++++ CONFIG.md | 185 +++++++++++++++++++++++++++++ README.md | 31 +++++ apply-config.js | 230 +++++++++++++++++++++++++++++++++++++ awesome-copilot.js | 93 +++++++++++++++ generate-config.js | 125 ++++++++++++++++++++ package.json | 3 + 8 files changed, 747 insertions(+) create mode 100644 .schemas/config.schema.yml create mode 100644 CONFIG.md create mode 100755 apply-config.js create mode 100755 awesome-copilot.js create mode 100755 generate-config.js diff --git a/.gitignore b/.gitignore index 4dd47dd..49c1b03 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,12 @@ Copilot-Processing.md # macOS system files .DS_Store + +# Configuration files (user-specific) +awesome-copilot.config.yml +*.config.yml +test-config.yml +test-apply-config.yml + +# Test output directories +test-output/ diff --git a/.schemas/config.schema.yml b/.schemas/config.schema.yml new file mode 100644 index 0000000..6f94643 --- /dev/null +++ b/.schemas/config.schema.yml @@ -0,0 +1,71 @@ +# Schema for awesome-copilot configuration file +$schema: "http://json-schema.org/draft-07/schema#" +title: "Awesome Copilot Configuration" +description: "Configuration file to enable/disable prompts, instructions, chat modes, and collections" +type: object + +properties: + version: + type: string + description: "Config file format version" + default: "1.0" + + project: + type: object + description: "Project-specific settings" + properties: + name: + type: string + description: "Project name" + description: + type: string + description: "Project description" + output_directory: + type: string + description: "Directory where files should be copied (default: .github)" + default: ".github" + + prompts: + type: object + description: "Enable/disable specific prompts" + additionalProperties: + type: boolean + + instructions: + type: object + description: "Enable/disable specific instructions" + additionalProperties: + type: boolean + + chatmodes: + type: object + description: "Enable/disable specific chat modes" + additionalProperties: + type: boolean + + collections: + type: object + description: "Enable/disable entire collections (will enable/disable all items in collection)" + additionalProperties: + type: boolean + +required: ["version"] + +examples: + - version: "1.0" + project: + name: "My Awesome Project" + description: "A project using awesome-copilot customizations" + output_directory: ".github" + prompts: + create-readme: true + generate-tests: false + instructions: + typescript-best-practices: true + testing-standards: false + chatmodes: + architect: true + dba: false + collections: + frontend-web-dev: true + csharp-dotnet-development: false \ No newline at end of file diff --git a/CONFIG.md b/CONFIG.md new file mode 100644 index 0000000..1b423e2 --- /dev/null +++ b/CONFIG.md @@ -0,0 +1,185 @@ +# Configuration File System + +The Awesome Copilot repository now supports a configuration file system that allows you to easily manage which prompts, instructions, chat modes, and collections are included in your project. + +## Quick Start + +### 1. Generate a Configuration File + +```bash +# Generate default configuration file +node awesome-copilot.js init + +# Or generate with a specific name +node awesome-copilot.js init my-project.config.yml +``` + +This creates a YAML configuration file with all available items set to `false` by default. + +### 2. Enable Desired Items + +Edit the configuration file to set items to `true` that you want to include: + +```yaml +version: "1.0" +project: + name: "My Project" + description: "A project using awesome-copilot customizations" + output_directory: ".github" +prompts: + create-readme: true + editorconfig: true + generate-tests: false +instructions: + typescript-best-practices: true + testing-standards: true + react: false +chatmodes: + architect: true + dba: false + specification: true +collections: + frontend-web-dev: true + csharp-dotnet-development: false +``` + +### 3. Apply Configuration + +```bash +# Apply default configuration file +node awesome-copilot.js apply + +# Or apply specific configuration file +node awesome-copilot.js apply my-project.config.yml +``` + +This will copy the enabled files to your project's `.github` directory (or the directory specified in the config). + +## Configuration File Format + +### Top-level Structure + +```yaml +version: "1.0" # Required: Config format version +project: # Optional: Project metadata + name: "My Project" # Project name + description: "Project desc" # Project description + output_directory: ".github" # Where to copy files (default: .github) +prompts: {} # Enable/disable prompts +instructions: {} # Enable/disable instructions +chatmodes: {} # Enable/disable chat modes +collections: {} # Enable/disable collections +``` + +### Individual Items + +Set any item to `true` to include it, `false` to exclude it: + +```yaml +prompts: + create-readme: true # Include this prompt + generate-tests: false # Exclude this prompt +``` + +### Collections + +Collections are special - when you enable a collection, it automatically includes all items in that collection: + +```yaml +collections: + frontend-web-dev: true # Includes all prompts, instructions, and chat modes in this collection +``` + +## Output Structure + +When you apply a configuration, files are organized as follows: + +``` +.github/ +├── copilot/ +│ ├── *.prompt.md # Prompts for /awesome-copilot commands +│ └── *.chatmode.md # Chat modes for VS Code +└── instructions/ + └── *.instructions.md # Instructions that auto-apply to coding +``` + +## NPM Scripts + +You can also use npm scripts instead of the CLI: + +```bash +# Generate configuration +npm run config:init + +# Apply configuration +npm run config:apply + +# Access CLI +npm run config help +``` + +## Examples + +### Frontend React Project + +```yaml +version: "1.0" +project: + name: "React Frontend" + output_directory: ".github" +collections: + frontend-web-dev: true +prompts: + create-readme: true + editorconfig: true +chatmodes: + specification: true +``` + +### Backend .NET Project + +```yaml +version: "1.0" +project: + name: ".NET API" + output_directory: ".github" +collections: + csharp-dotnet-development: true +instructions: + testing-standards: true +prompts: + create-specification: true +``` + +### Full Stack Project + +```yaml +version: "1.0" +project: + name: "Full Stack App" + output_directory: ".github" +collections: + frontend-web-dev: true + csharp-dotnet-development: true + database-data-management: true +chatmodes: + architect: true + specification: true +``` + +## Migration from Manual Approach + +If you were previously copying files manually: + +1. Remove manually copied files from your `.github` directory +2. Run `node awesome-copilot.js init` to create a config file +3. Edit the config to enable the same items you were using manually +4. Run `node awesome-copilot.js apply` to get a clean, managed setup + +## Benefits + +- **Centralized Management**: One file controls all your Copilot customizations +- **Version Control Friendly**: Config file tracks what's enabled in your project +- **Easy Updates**: Re-run apply command after pulling awesome-copilot updates +- **Collection Support**: Enable entire curated sets with one setting +- **Clean Organization**: Files are organized in proper directory structure \ No newline at end of file diff --git a/README.md b/README.md index 26a678e..62d626f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,37 @@ To make it easy to add these customizations to your editor, we have created a [M ## 🔧 How to Use +There are two ways to use awesome-copilot customizations in your project: + +### 🎛️ Configuration File Approach (Recommended) + +Use our configuration system to manage all customizations in one place: + +1. **Clone the repository** to your local machine or include it as a git submodule +2. **Generate a configuration file** with all available options: + ```bash + node awesome-copilot.js init + ``` +3. **Edit the configuration** to enable the items you want: + ```yaml + collections: + frontend-web-dev: true # Enable entire collection + prompts: + create-readme: true # Enable specific prompts + instructions: + typescript-best-practices: true + ``` +4. **Apply the configuration** to copy files to your project: + ```bash + node awesome-copilot.js apply + ``` + +See [CONFIG.md](CONFIG.md) for detailed configuration documentation. + +### 📁 Manual File Approach + +Browse the collections and manually copy files you want to use: + ### 🎯 Prompts Use the `/` command in GitHub Copilot Chat to access prompts: ``` diff --git a/apply-config.js b/apply-config.js new file mode 100755 index 0000000..b535f5d --- /dev/null +++ b/apply-config.js @@ -0,0 +1,230 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const path = require("path"); +const { parseCollectionYaml } = require("./yaml-parser"); + +/** + * Simple YAML parser for configuration files + */ +function parseConfigYaml(filePath) { + try { + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n"); + const result = {}; + let currentSection = null; + + for (const line of lines) { + const trimmed = line.trim(); + + // Skip comments and empty lines + if (!trimmed || trimmed.startsWith("#")) continue; + + // Handle key-value pairs + if (trimmed.includes(":")) { + const colonIndex = trimmed.indexOf(":"); + const key = trimmed.substring(0, colonIndex).trim(); + let value = trimmed.substring(colonIndex + 1).trim(); + + // Remove quotes if present + if ((value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'"))) { + value = value.slice(1, -1); + } + + // Handle sections (no value) + if (!value) { + currentSection = key; + if (!result[currentSection]) { + result[currentSection] = {}; + } + } else { + // Handle boolean values + if (value === "true") value = true; + else if (value === "false") value = false; + + if (currentSection) { + result[currentSection][key] = value; + } else { + result[key] = value; + } + } + } + } + + return result; + } catch (error) { + console.error(`Error parsing config file ${filePath}: ${error.message}`); + return null; + } +} + +/** + * Apply configuration and copy enabled files to project + */ +async function applyConfig(configPath = "awesome-copilot.config.yml") { + if (!fs.existsSync(configPath)) { + console.error(`Configuration file not found: ${configPath}`); + console.log("Run 'node generate-config.js' to create a configuration file first."); + process.exit(1); + } + + const config = parseConfigYaml(configPath); + if (!config) { + console.error("Failed to parse configuration file"); + process.exit(1); + } + + console.log("Applying awesome-copilot configuration..."); + + const rootDir = __dirname; + const outputDir = config.project?.output_directory || ".github"; + + // Create output directory structure + ensureDirectoryExists(outputDir); + ensureDirectoryExists(path.join(outputDir, "copilot")); + ensureDirectoryExists(path.join(outputDir, "instructions")); + + let copiedCount = 0; + const summary = { + prompts: 0, + instructions: 0, + chatmodes: 0, + collections: 0 + }; + + // Process collections first (they can enable individual items) + const enabledItems = new Set(); + if (config.collections) { + for (const [collectionName, enabled] of Object.entries(config.collections)) { + if (enabled) { + const collectionPath = path.join(rootDir, "collections", `${collectionName}.collection.yml`); + if (fs.existsSync(collectionPath)) { + const collection = parseCollectionYaml(collectionPath); + if (collection && collection.items) { + collection.items.forEach(item => { + enabledItems.add(item.path); + }); + summary.collections++; + console.log(`✓ Enabled collection: ${collectionName} (${collection.items.length} items)`); + } + } + } + } + } + + // Process prompts + if (config.prompts) { + for (const [promptName, enabled] of Object.entries(config.prompts)) { + if (enabled) { + const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`); + if (fs.existsSync(sourcePath)) { + const destPath = path.join(outputDir, "copilot", `${promptName}.prompt.md`); + copyFile(sourcePath, destPath); + copiedCount++; + summary.prompts++; + } + } + } + } + + // Process instructions + if (config.instructions) { + for (const [instructionName, enabled] of Object.entries(config.instructions)) { + if (enabled) { + const sourcePath = path.join(rootDir, "instructions", `${instructionName}.instructions.md`); + if (fs.existsSync(sourcePath)) { + const destPath = path.join(outputDir, "instructions", `${instructionName}.instructions.md`); + copyFile(sourcePath, destPath); + copiedCount++; + summary.instructions++; + } + } + } + } + + // Process chat modes + if (config.chatmodes) { + for (const [chatmodeName, enabled] of Object.entries(config.chatmodes)) { + if (enabled) { + const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`); + if (fs.existsSync(sourcePath)) { + const destPath = path.join(outputDir, "copilot", `${chatmodeName}.chatmode.md`); + copyFile(sourcePath, destPath); + copiedCount++; + summary.chatmodes++; + } + } + } + } + + // Process items from enabled collections + for (const itemPath of enabledItems) { + const sourcePath = path.join(rootDir, itemPath); + if (fs.existsSync(sourcePath)) { + const fileName = path.basename(itemPath); + let destPath; + + if (fileName.endsWith('.prompt.md') || fileName.endsWith('.chatmode.md')) { + destPath = path.join(outputDir, "copilot", fileName); + } else if (fileName.endsWith('.instructions.md')) { + destPath = path.join(outputDir, "instructions", fileName); + } + + if (destPath && !fs.existsSync(destPath)) { + copyFile(sourcePath, destPath); + copiedCount++; + } + } + } + + // Generate summary + console.log("\n" + "=".repeat(50)); + console.log("Configuration applied successfully!"); + console.log("=".repeat(50)); + console.log(`📂 Output directory: ${outputDir}`); + console.log(`📝 Total files copied: ${copiedCount}`); + console.log(`🎯 Prompts: ${summary.prompts}`); + console.log(`📋 Instructions: ${summary.instructions}`); + console.log(`💭 Chat modes: ${summary.chatmodes}`); + console.log(`📦 Collections: ${summary.collections}`); + + if (config.project?.name) { + console.log(`🏷️ Project: ${config.project.name}`); + } + + console.log("\nNext steps:"); + console.log("1. Add the files to your version control system"); + console.log("2. Use prompts with /awesome-copilot command in GitHub Copilot Chat"); + console.log("3. Instructions will automatically apply to your coding"); + console.log("4. Import chat modes in VS Code settings"); +} + +/** + * Ensure directory exists, create if it doesn't + */ +function ensureDirectoryExists(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + console.log(`📁 Created directory: ${dirPath}`); + } +} + +/** + * Copy file from source to destination + */ +function copyFile(sourcePath, destPath) { + fs.copyFileSync(sourcePath, destPath); + console.log(`✓ Copied: ${path.basename(sourcePath)}`); +} + +// CLI usage +if (require.main === module) { + const configPath = process.argv[2] || "awesome-copilot.config.yml"; + applyConfig(configPath).catch(error => { + console.error("Error applying configuration:", error.message); + process.exit(1); + }); +} + +module.exports = { applyConfig, parseConfigYaml }; \ No newline at end of file diff --git a/awesome-copilot.js b/awesome-copilot.js new file mode 100755 index 0000000..84f1804 --- /dev/null +++ b/awesome-copilot.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node + +const { generateConfig } = require("./generate-config"); +const { applyConfig } = require("./apply-config"); + +const commands = { + init: { + description: "Generate a new configuration file", + usage: "awesome-copilot init [config-file]", + action: async (args) => { + const configFile = args[0] || "awesome-copilot.config.yml"; + generateConfig(configFile); + } + }, + + apply: { + description: "Apply configuration and copy files to project", + usage: "awesome-copilot apply [config-file]", + action: async (args) => { + const configFile = args[0] || "awesome-copilot.config.yml"; + await applyConfig(configFile); + } + }, + + help: { + description: "Show help information", + usage: "awesome-copilot help", + action: () => { + showHelp(); + } + } +}; + +function showHelp() { + console.log("🤖 Awesome GitHub Copilot Configuration Tool"); + console.log("=".repeat(50)); + console.log(""); + console.log("Usage: awesome-copilot [options]"); + console.log(""); + console.log("Commands:"); + + for (const [name, cmd] of Object.entries(commands)) { + console.log(` ${name.padEnd(10)} ${cmd.description}`); + console.log(` ${' '.repeat(10)} ${cmd.usage}`); + console.log(""); + } + + console.log("Examples:"); + console.log(" awesome-copilot init # Create default config file"); + console.log(" awesome-copilot init my-config.yml # Create named config file"); + console.log(" awesome-copilot apply # Apply default config"); + console.log(" awesome-copilot apply my-config.yml # Apply specific config"); + console.log(""); + console.log("Workflow:"); + console.log(" 1. Run 'awesome-copilot init' to create a configuration file"); + console.log(" 2. Edit the configuration file to enable desired items"); + console.log(" 3. Run 'awesome-copilot apply' to copy files to your project"); +} + +function showError(message) { + console.error(`❌ Error: ${message}`); + console.log(""); + console.log("Run 'awesome-copilot help' for usage information."); + process.exit(1); +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + showHelp(); + return; + } + + const command = args[0]; + const commandArgs = args.slice(1); + + if (!commands[command]) { + showError(`Unknown command: ${command}`); + } + + try { + await commands[command].action(commandArgs); + } catch (error) { + showError(error.message); + } +} + +if (require.main === module) { + main(); +} + +module.exports = { main }; \ No newline at end of file diff --git a/generate-config.js b/generate-config.js new file mode 100755 index 0000000..bd4678c --- /dev/null +++ b/generate-config.js @@ -0,0 +1,125 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const path = require("path"); + +/** + * Generate a configuration file with all available options + */ +function generateConfig(outputPath = "awesome-copilot.config.yml") { + const rootDir = __dirname; + + // Get all available items + const prompts = getAvailableItems(path.join(rootDir, "prompts"), ".prompt.md"); + const instructions = getAvailableItems(path.join(rootDir, "instructions"), ".instructions.md"); + const chatmodes = getAvailableItems(path.join(rootDir, "chatmodes"), ".chatmode.md"); + const collections = getAvailableItems(path.join(rootDir, "collections"), ".collection.yml"); + + // Create config structure + const config = { + version: "1.0", + project: { + name: "My Project", + description: "A project using awesome-copilot customizations", + output_directory: ".github" + }, + prompts: {}, + instructions: {}, + chatmodes: {}, + collections: {} + }; + + // Populate with all items disabled by default (user can enable what they want) + prompts.forEach(item => { + config.prompts[item] = false; + }); + + instructions.forEach(item => { + config.instructions[item] = false; + }); + + chatmodes.forEach(item => { + config.chatmodes[item] = false; + }); + + collections.forEach(item => { + config.collections[item] = false; + }); + + // Convert to YAML format manually (since we don't want to add dependencies) + const yamlContent = objectToYaml(config); + + // Add header comment + const header = `# Awesome Copilot Configuration File +# Generated on ${new Date().toISOString()} +# +# This file allows you to enable/disable specific prompts, instructions, +# chat modes, and collections for your project. +# +# Set items to 'true' to include them in your project +# Set items to 'false' to exclude them +# +# After configuring, run: node apply-config.js +# + +`; + + const fullContent = header + yamlContent; + + fs.writeFileSync(outputPath, fullContent); + console.log(`Configuration file generated: ${outputPath}`); + console.log(`Found ${prompts.length} prompts, ${instructions.length} instructions, ${chatmodes.length} chat modes, ${collections.length} collections`); + console.log("\nNext steps:"); + console.log("1. Edit the configuration file to enable desired items"); + console.log("2. Run: node apply-config.js to apply the configuration"); +} + +/** + * Get all available items in a directory + */ +function getAvailableItems(directory, extension) { + if (!fs.existsSync(directory)) { + return []; + } + + return fs.readdirSync(directory) + .filter(file => file.endsWith(extension)) + .map(file => path.basename(file, extension)) + .sort(); +} + +/** + * Convert object to YAML format (simple implementation) + */ +function objectToYaml(obj, indent = 0) { + const spaces = " ".repeat(indent); + let yaml = ""; + + for (const [key, value] of Object.entries(obj)) { + if (typeof value === "object" && value !== null) { + yaml += `${spaces}${key}:\n`; + yaml += objectToYaml(value, indent + 1); + } else { + const valueStr = typeof value === "string" ? `"${value}"` : value; + yaml += `${spaces}${key}: ${valueStr}\n`; + } + } + + return yaml; +} + +// CLI usage +if (require.main === module) { + const outputPath = process.argv[2] || "awesome-copilot.config.yml"; + generateConfig(outputPath); +} + +module.exports = { generateConfig, getAvailableItems }; + +// CLI usage +if (require.main === module) { + const outputPath = process.argv[2] || "awesome-copilot.config.yml"; + generateConfig(outputPath); +} + +module.exports = { generateConfig, getAvailableItems }; \ No newline at end of file diff --git a/package.json b/package.json index e37dca1..8adc6c4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "main": "update-readme.js", "scripts": { "build": "node update-readme.js", + "config:init": "node generate-config.js", + "config:apply": "node apply-config.js", + "config": "node awesome-copilot.js", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", "contributors:check": "all-contributors check"