Implement effective state computation and update apply logic

Co-authored-by: AstroSteveo <34114851+AstroSteveo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-09-21 22:41:27 +00:00
parent ea6beac100
commit 18846b91f4
3 changed files with 167 additions and 69 deletions

View File

@ -100,8 +100,28 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
collections: 0
};
// Process collections first (they can enable individual items)
const enabledItems = new Set();
// Import config manager for effective state computation
const { computeEffectiveItemStates } = require("./config-manager");
// Compute effective states using precedence rules
const effectiveStates = computeEffectiveItemStates(config);
// Create sets of effectively enabled items for performance
const effectivelyEnabledSets = {
prompts: new Set(),
instructions: new Set(),
chatmodes: new Set()
};
for (const section of ["prompts", "instructions", "chatmodes"]) {
for (const [itemName, state] of Object.entries(effectiveStates[section])) {
if (state.enabled) {
effectivelyEnabledSets[section].add(itemName);
}
}
}
// Count enabled collections for summary
if (config.collections) {
for (const [collectionName, enabled] of Object.entries(config.collections)) {
if (enabled) {
@ -109,9 +129,6 @@ async function applyConfig(configPath = "awesome-copilot.config.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)`);
}
@ -120,70 +137,36 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
}
}
// 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, "prompts", `${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, "chatmodes", `${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);
// Process prompts using effective states
for (const promptName of effectivelyEnabledSets.prompts) {
const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`);
if (fs.existsSync(sourcePath)) {
const fileName = path.basename(itemPath);
let destPath;
if (fileName.endsWith('.prompt.md')) {
destPath = path.join(outputDir, "prompts", fileName);
} else if (fileName.endsWith('.chatmode.md')) {
destPath = path.join(outputDir, "chatmodes", fileName);
} else if (fileName.endsWith('.instructions.md')) {
destPath = path.join(outputDir, "instructions", fileName);
}
if (destPath && !fs.existsSync(destPath)) {
copyFile(sourcePath, destPath);
copiedCount++;
}
const destPath = path.join(outputDir, "prompts", `${promptName}.prompt.md`);
copyFile(sourcePath, destPath);
copiedCount++;
summary.prompts++;
}
}
// Process instructions using effective states
for (const instructionName of effectivelyEnabledSets.instructions) {
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 using effective states
for (const chatmodeName of effectivelyEnabledSets.chatmodes) {
const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`);
if (fs.existsSync(sourcePath)) {
const destPath = path.join(outputDir, "chatmodes", `${chatmodeName}.chatmode.md`);
copyFile(sourcePath, destPath);
copiedCount++;
summary.chatmodes++;
}
}

View File

@ -158,6 +158,90 @@ function getAllAvailableItems(type) {
return getAvailableItems(path.join(__dirname, meta.dir), meta.ext);
}
/**
* Compute effective item states respecting explicit overrides over collections
* @param {Object} config - Configuration object with sections
* @returns {Object} Effective states for each section with { itemName: { enabled: boolean, reason: string } }
*/
function computeEffectiveItemStates(config) {
const { parseCollectionYaml } = require("./yaml-parser");
const effectiveStates = {
prompts: {},
instructions: {},
chatmodes: {}
};
// First, collect all items enabled by collections
const collectionEnabledItems = {
prompts: new Set(),
instructions: new Set(),
chatmodes: new Set()
};
if (config.collections) {
for (const [collectionName, enabled] of Object.entries(config.collections)) {
if (enabled === true) {
const collectionPath = path.join(__dirname, "collections", `${collectionName}.collection.yml`);
if (fs.existsSync(collectionPath)) {
const collection = parseCollectionYaml(collectionPath);
if (collection && collection.items) {
collection.items.forEach(item => {
// Extract item name from path - remove directory and all extensions
const itemName = path.basename(item.path).replace(/\.(prompt|instructions|chatmode)\.md$/, '');
if (item.kind === "prompt") {
collectionEnabledItems.prompts.add(itemName);
} else if (item.kind === "instruction") {
collectionEnabledItems.instructions.add(itemName);
} else if (item.kind === "chat-mode") {
collectionEnabledItems.chatmodes.add(itemName);
}
});
}
}
}
}
}
// For each section, compute effective states
for (const section of ["prompts", "instructions", "chatmodes"]) {
const sectionConfig = config[section] || {};
const collectionEnabled = collectionEnabledItems[section];
// Get all available items for this section
const availableItems = getAllAvailableItems(section);
for (const itemName of availableItems) {
const explicitValue = sectionConfig[itemName];
const isEnabledByCollection = collectionEnabled.has(itemName);
// Precedence rules:
// 1. If explicitly set to true or false, use that value
// 2. If undefined and enabled by collection, use true
// 3. Otherwise, use false
let enabled = false;
let reason = "disabled";
if (explicitValue === true) {
enabled = true;
reason = "explicit";
} else if (explicitValue === false) {
enabled = false;
reason = "explicit";
} else if (explicitValue === undefined && isEnabledByCollection) {
enabled = true;
reason = "collection";
}
effectiveStates[section][itemName] = { enabled, reason };
}
}
return effectiveStates;
}
module.exports = {
DEFAULT_CONFIG_PATH,
CONFIG_SECTIONS,
@ -168,5 +252,6 @@ module.exports = {
ensureConfigStructure,
sortObjectKeys,
countEnabledItems,
getAllAvailableItems
getAllAvailableItems,
computeEffectiveItemStates
};

30
test-effective-config.yml Normal file
View File

@ -0,0 +1,30 @@
# Awesome Copilot Configuration File
# Manual test for effective state computation
#
# Testing precedence rules with undefined values
version: "1.0"
project:
name: "Test Project"
description: "Testing effective state precedence"
output_directory: ".awesome-copilot"
collections:
testing-automation: true
prompts:
playwright-generate-test: true
# Note: playwright-explore-website is not defined (undefined)
# Note: csharp-nunit is not defined (undefined)
# Note: java-junit is not defined (undefined)
ai-prompt-engineering-safety-review: false
instructions:
# Note: playwright-typescript is not defined (undefined)
# Note: playwright-python is not defined (undefined)
chatmodes:
# Note: tdd-red is not defined (undefined)
# Note: tdd-green is not defined (undefined)
# Note: tdd-refactor is not defined (undefined)
# Note: playwright-tester is not defined (undefined)