Implement effective state computation and update apply logic
Co-authored-by: AstroSteveo <34114851+AstroSteveo@users.noreply.github.com>
This commit is contained in:
parent
ea6beac100
commit
18846b91f4
@ -100,8 +100,28 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
collections: 0
|
collections: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process collections first (they can enable individual items)
|
// Import config manager for effective state computation
|
||||||
const enabledItems = new Set();
|
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) {
|
if (config.collections) {
|
||||||
for (const [collectionName, enabled] of Object.entries(config.collections)) {
|
for (const [collectionName, enabled] of Object.entries(config.collections)) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
@ -109,9 +129,6 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
if (fs.existsSync(collectionPath)) {
|
if (fs.existsSync(collectionPath)) {
|
||||||
const collection = parseCollectionYaml(collectionPath);
|
const collection = parseCollectionYaml(collectionPath);
|
||||||
if (collection && collection.items) {
|
if (collection && collection.items) {
|
||||||
collection.items.forEach(item => {
|
|
||||||
enabledItems.add(item.path);
|
|
||||||
});
|
|
||||||
summary.collections++;
|
summary.collections++;
|
||||||
console.log(`✓ Enabled collection: ${collectionName} (${collection.items.length} items)`);
|
console.log(`✓ Enabled collection: ${collectionName} (${collection.items.length} items)`);
|
||||||
}
|
}
|
||||||
@ -120,10 +137,8 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process prompts
|
// Process prompts using effective states
|
||||||
if (config.prompts) {
|
for (const promptName of effectivelyEnabledSets.prompts) {
|
||||||
for (const [promptName, enabled] of Object.entries(config.prompts)) {
|
|
||||||
if (enabled) {
|
|
||||||
const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`);
|
const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "prompts", `${promptName}.prompt.md`);
|
const destPath = path.join(outputDir, "prompts", `${promptName}.prompt.md`);
|
||||||
@ -132,13 +147,9 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
summary.prompts++;
|
summary.prompts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process instructions
|
// Process instructions using effective states
|
||||||
if (config.instructions) {
|
for (const instructionName of effectivelyEnabledSets.instructions) {
|
||||||
for (const [instructionName, enabled] of Object.entries(config.instructions)) {
|
|
||||||
if (enabled) {
|
|
||||||
const sourcePath = path.join(rootDir, "instructions", `${instructionName}.instructions.md`);
|
const sourcePath = path.join(rootDir, "instructions", `${instructionName}.instructions.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "instructions", `${instructionName}.instructions.md`);
|
const destPath = path.join(outputDir, "instructions", `${instructionName}.instructions.md`);
|
||||||
@ -147,13 +158,9 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
summary.instructions++;
|
summary.instructions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process chat modes
|
// Process chat modes using effective states
|
||||||
if (config.chatmodes) {
|
for (const chatmodeName of effectivelyEnabledSets.chatmodes) {
|
||||||
for (const [chatmodeName, enabled] of Object.entries(config.chatmodes)) {
|
|
||||||
if (enabled) {
|
|
||||||
const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
const destPath = path.join(outputDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
||||||
@ -162,30 +169,6 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
summary.chatmodes++;
|
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')) {
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate summary
|
// Generate summary
|
||||||
console.log("\n" + "=".repeat(50));
|
console.log("\n" + "=".repeat(50));
|
||||||
|
|||||||
@ -158,6 +158,90 @@ function getAllAvailableItems(type) {
|
|||||||
return getAvailableItems(path.join(__dirname, meta.dir), meta.ext);
|
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 = {
|
module.exports = {
|
||||||
DEFAULT_CONFIG_PATH,
|
DEFAULT_CONFIG_PATH,
|
||||||
CONFIG_SECTIONS,
|
CONFIG_SECTIONS,
|
||||||
@ -168,5 +252,6 @@ module.exports = {
|
|||||||
ensureConfigStructure,
|
ensureConfigStructure,
|
||||||
sortObjectKeys,
|
sortObjectKeys,
|
||||||
countEnabledItems,
|
countEnabledItems,
|
||||||
getAllAvailableItems
|
getAllAvailableItems,
|
||||||
|
computeEffectiveItemStates
|
||||||
};
|
};
|
||||||
|
|||||||
30
test-effective-config.yml
Normal file
30
test-effective-config.yml
Normal 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)
|
||||||
Loading…
x
Reference in New Issue
Block a user