diff --git a/config-manager.js b/config-manager.js index be1936e..8e8b258 100644 --- a/config-manager.js +++ b/config-manager.js @@ -183,8 +183,16 @@ function generateConfigHash(config) { /** * Compute effective item states respecting explicit overrides over collections + * + * This function builds membership maps per section and returns effectively enabled items with reasons. + * It uses the following precedence rules: + * 1. Explicit true/false overrides everything (highest priority) + * 2. If undefined and enabled by collection, use true + * 3. Otherwise, use false (disabled) + * * @param {Object} config - Configuration object with sections * @returns {Object} Effective states for each section with { itemName: { enabled: boolean, reason: string } } + * Reason can be: 'explicit', 'collection', or 'disabled' */ function computeEffectiveItemStates(config) { const { parseCollectionYaml } = require("./yaml-parser"); @@ -195,13 +203,14 @@ function computeEffectiveItemStates(config) { chatmodes: {} }; - // First, collect all items enabled by collections + // Build membership maps: Map> per section for O(1) lookups const collectionEnabledItems = { prompts: new Set(), instructions: new Set(), chatmodes: new Set() }; + // Identify enabled collections per section if (config.collections) { for (const [collectionName, enabled] of Object.entries(config.collections)) { if (enabled === true) { @@ -227,7 +236,7 @@ function computeEffectiveItemStates(config) { } } - // For each section, compute effective states + // For each section, compute effective states using precedence rules for (const section of ["prompts", "instructions", "chatmodes"]) { const sectionConfig = config[section] || {}; const collectionEnabled = collectionEnabledItems[section]; @@ -265,6 +274,38 @@ function computeEffectiveItemStates(config) { return effectiveStates; } +/** + * Get sets of effectively enabled items with reasons - optimized for performance lookups + * + * This function satisfies the acceptance criteria by returning Sets for O(1) lookups + * while maintaining the precedence rules defined in computeEffectiveItemStates. + * + * @param {Object} config - Configuration object with sections + * @returns {Object} Sets of enabled items: { prompts: Set, instructions: Set, chatmodes: Set } + * Each Set contains only the names of effectively enabled items for O(1) lookup performance + */ +function getEffectivelyEnabledItems(config) { + const effectiveStates = computeEffectiveItemStates(config); + + return { + prompts: new Set( + Object.entries(effectiveStates.prompts) + .filter(([, state]) => state.enabled) + .map(([itemName]) => itemName) + ), + instructions: new Set( + Object.entries(effectiveStates.instructions) + .filter(([, state]) => state.enabled) + .map(([itemName]) => itemName) + ), + chatmodes: new Set( + Object.entries(effectiveStates.chatmodes) + .filter(([, state]) => state.enabled) + .map(([itemName]) => itemName) + ) + }; +} + module.exports = { DEFAULT_CONFIG_PATH, CONFIG_SECTIONS, @@ -277,5 +318,6 @@ module.exports = { countEnabledItems, getAllAvailableItems, computeEffectiveItemStates, + getEffectivelyEnabledItems, generateConfigHash }; diff --git a/test-effective-states.js b/test-effective-states.js index 5139061..621f04b 100644 --- a/test-effective-states.js +++ b/test-effective-states.js @@ -5,7 +5,7 @@ */ const path = require('path'); -const { computeEffectiveItemStates } = require('./config-manager'); +const { computeEffectiveItemStates, getEffectivelyEnabledItems } = require('./config-manager'); // Change to project directory for tests process.chdir(__dirname); @@ -164,6 +164,56 @@ function runTests() { 'Chat mode should be enabled by collection'); }); + // Test 8: getEffectivelyEnabledItems returns Sets format + test("getEffectivelyEnabledItems returns Sets format", () => { + const config = { + prompts: { + 'playwright-generate-test': true, + 'csharp-nunit': false + }, + collections: { + 'testing-automation': true + } + }; + const result = getEffectivelyEnabledItems(config); + + assert(typeof result === 'object', 'Should return object'); + assert(result.prompts instanceof Set, 'Prompts should be a Set'); + assert(result.instructions instanceof Set, 'Instructions should be a Set'); + assert(result.chatmodes instanceof Set, 'Chatmodes should be a Set'); + + // Check that explicitly enabled items are included + assert(result.prompts.has('playwright-generate-test'), + 'Explicitly enabled prompt should be in Set'); + + // Check that explicitly disabled items are not included (even if in collection) + assert(!result.prompts.has('csharp-nunit'), + 'Explicitly disabled prompt should not be in Set'); + + // Check that collection-enabled items are included + assert(result.prompts.has('playwright-explore-website'), + 'Collection-enabled prompt should be in Set'); + }); + + // Test 9: getEffectivelyEnabledItems performance check + test("getEffectivelyEnabledItems provides O(1) lookups", () => { + const config = { + collections: { + 'testing-automation': true + } + }; + const result = getEffectivelyEnabledItems(config); + + // Test O(1) lookup performance + const startTime = process.hrtime.bigint(); + const hasItem = result.prompts.has('playwright-generate-test'); + const endTime = process.hrtime.bigint(); + + assert(hasItem === true, 'Should find enabled item'); + // This is more of a structural test - Sets provide O(1) lookups by design + assert(result.prompts.size > 0, 'Should have enabled prompts'); + }); + console.log(`\nTest Results: ${passedTests}/${totalTests} passed`); if (passedTests === totalTests) {