Implement YAML configuration system for toggling prompts, instructions, and chat modes

Co-authored-by: AstroSteveo <34114851+AstroSteveo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-09-20 16:34:40 +00:00
parent 30c7e48ec7
commit 61928bca42
8 changed files with 747 additions and 0 deletions

9
.gitignore vendored
View File

@ -4,3 +4,12 @@ Copilot-Processing.md
# macOS system files # macOS system files
.DS_Store .DS_Store
# Configuration files (user-specific)
awesome-copilot.config.yml
*.config.yml
test-config.yml
test-apply-config.yml
# Test output directories
test-output/

View File

@ -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

185
CONFIG.md Normal file
View File

@ -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

View File

@ -46,6 +46,37 @@ To make it easy to add these customizations to your editor, we have created a [M
## 🔧 How to Use ## 🔧 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 ### 🎯 Prompts
Use the `/` command in GitHub Copilot Chat to access prompts: Use the `/` command in GitHub Copilot Chat to access prompts:
``` ```

230
apply-config.js Executable file
View File

@ -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 };

93
awesome-copilot.js Executable file
View File

@ -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 <command> [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 };

125
generate-config.js Executable file
View File

@ -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 };

View File

@ -5,6 +5,9 @@
"main": "update-readme.js", "main": "update-readme.js",
"scripts": { "scripts": {
"build": "node update-readme.js", "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:add": "all-contributors add",
"contributors:generate": "all-contributors generate", "contributors:generate": "all-contributors generate",
"contributors:check": "all-contributors check" "contributors:check": "all-contributors check"