Implement enhanced CLI list command with detailed reason display and collection counts

Co-authored-by: AstroSteveo <34114851+AstroSteveo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-09-23 23:59:17 +00:00
parent 88e13bc74e
commit 25e6141857
5 changed files with 105 additions and 19 deletions

View File

@ -149,8 +149,16 @@ function handleListCommand(rawArgs) {
const availableItems = getAllAvailableItems(section);
// Count effectively enabled items
const effectivelyEnabled = Object.values(effectiveStates[section] || {})
let effectivelyEnabled;
if (section === "collections") {
// For collections, count explicitly enabled ones
effectivelyEnabled = Object.values(sanitizedConfig[section] || {})
.filter(value => value === true).length;
} else {
// For other sections, count effectively enabled items
effectivelyEnabled = Object.values(effectiveStates[section] || {})
.filter(state => state.enabled).length;
}
const { totalCharacters } = calculateSectionFootprint(section, sanitizedConfig[section]);
const headingParts = [
@ -170,20 +178,40 @@ function handleListCommand(rawArgs) {
// Show items with effective state and reason
if (section === "collections") {
// Collections show simple enabled/disabled
// Collections show simple enabled/disabled with count of effectively enabled items
availableItems.forEach(itemName => {
const isEnabled = Boolean(sanitizedConfig[section]?.[itemName]);
console.log(` [${isEnabled ? "✓" : " "}] ${itemName}`);
// Count how many items this collection would enable
let enabledCount = 0;
if (isEnabled) {
// Simulate what would happen if only this collection was enabled
const testConfig = { collections: { [itemName]: true } };
const testEffectiveStates = computeEffectiveItemStates(testConfig);
for (const sectionName of ["prompts", "instructions", "chatmodes"]) {
enabledCount += Object.values(testEffectiveStates[sectionName] || {})
.filter(state => state.enabled && state.reason === "collection").length;
}
}
const countText = isEnabled ? ` (${enabledCount} items effectively enabled)` : "";
console.log(` [${isEnabled ? "✓" : " "}] ${itemName}${countText}`);
});
} else {
// Other sections show effective state with reason
// Other sections show effective state with detailed reason
availableItems.forEach(itemName => {
const effectiveState = effectiveStates[section]?.[itemName];
if (effectiveState) {
const symbol = effectiveState.enabled ? "✓" : " ";
const reasonText = effectiveState.reason === "explicit"
? ` (${effectiveState.reason})`
: effectiveState.enabled ? ` (${effectiveState.reason})` : "";
let reasonText = "";
if (effectiveState.reason === "explicit") {
reasonText = ` (explicit:${effectiveState.enabled})`;
} else if (effectiveState.reason === "collection" && effectiveState.collections && effectiveState.collections.length > 0) {
reasonText = ` (via:[${effectiveState.collections.join(',')}])`;
}
console.log(` [${symbol}] ${itemName}${reasonText}`);
} else {
console.log(` [ ] ${itemName}`);

View File

@ -203,14 +203,21 @@ function computeEffectiveItemStates(config) {
chatmodes: {}
};
// Build membership maps: Map<itemName, Set<collectionName>> per section for O(1) lookups
// Build detailed membership maps: Map<itemName, Set<collectionName>> per section
const collectionMemberships = {
prompts: new Map(),
instructions: new Map(),
chatmodes: new Map()
};
// Build simple enabled sets for O(1) lookups
const collectionEnabledItems = {
prompts: new Set(),
instructions: new Set(),
chatmodes: new Set()
};
// Identify enabled collections per section
// Identify enabled collections per section and track memberships
if (config.collections) {
for (const [collectionName, enabled] of Object.entries(config.collections)) {
if (enabled === true) {
@ -222,12 +229,24 @@ function computeEffectiveItemStates(config) {
// Extract item name from path - remove directory and all extensions
const itemName = path.basename(item.path).replace(/\.(prompt|instructions|chatmode)\.md$/, '');
let sectionName;
if (item.kind === "prompt") {
collectionEnabledItems.prompts.add(itemName);
sectionName = "prompts";
} else if (item.kind === "instruction") {
collectionEnabledItems.instructions.add(itemName);
sectionName = "instructions";
} else if (item.kind === "chat-mode") {
collectionEnabledItems.chatmodes.add(itemName);
sectionName = "chatmodes";
}
if (sectionName) {
// Track which collections enable this item
if (!collectionMemberships[sectionName].has(itemName)) {
collectionMemberships[sectionName].set(itemName, new Set());
}
collectionMemberships[sectionName].get(itemName).add(collectionName);
// Add to enabled set for O(1) lookups
collectionEnabledItems[sectionName].add(itemName);
}
});
}
@ -247,6 +266,7 @@ function computeEffectiveItemStates(config) {
for (const itemName of availableItems) {
const explicitValue = sectionConfig[itemName];
const isEnabledByCollection = collectionEnabled.has(itemName);
const enablingCollections = collectionMemberships[section].get(itemName) || new Set();
// Precedence rules:
// 1. If explicitly set to true or false, use that value
@ -255,6 +275,7 @@ function computeEffectiveItemStates(config) {
let enabled = false;
let reason = "disabled";
let collections = [];
if (explicitValue === true) {
enabled = true;
@ -265,9 +286,10 @@ function computeEffectiveItemStates(config) {
} else if (explicitValue === undefined && isEnabledByCollection) {
enabled = true;
reason = "collection";
collections = Array.from(enablingCollections).sort();
}
effectiveStates[section][itemName] = { enabled, reason };
effectiveStates[section][itemName] = { enabled, reason, collections };
}
}

36
final-test.yml Normal file
View File

@ -0,0 +1,36 @@
# Awesome Copilot Configuration File
# Generated on 2025-09-23T23:58:14.331Z
#
# This file uses effective state precedence:
# 1. Explicit item settings (true/false) override everything
# 2. Items not listed inherit from enabled collections
# 3. Otherwise items are disabled
#
# To use:
# - Enable collections for curated sets of related items
# - Explicitly set individual items to true/false to override collections
# - Items not mentioned will follow collection settings
#
# After configuring, run: awesome-copilot apply
#
version: "1.0"
project:
name: "My Project"
description: "A project using awesome-copilot customizations"
output_directory: ".github"
prompts:
create-readme: true
csharp-nunit: false
instructions:
chatmodes:
collections:
azure-cloud-development: false
csharp-dotnet-development: false
database-data-management: false
devops-oncall: false
frontend-web-dev: false
project-planning: false
security-best-practices: true
technical-spike: false
testing-automation: true

View File

@ -92,7 +92,7 @@ async function runTests() {
const result = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
assert(result.success, 'List should succeed');
assert(result.stdout.includes('(collection)'), 'Should show collection reason');
assert(result.stdout.includes('(via:[testing-automation])'), 'Should show collection reason');
assert(result.stdout.includes('[✓]'), 'Should show enabled items');
});
@ -105,7 +105,7 @@ async function runTests() {
assert(result.success, 'Individual toggle should succeed');
const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
assert(listResult.stdout.includes('playwright-generate-test (explicit)'),
assert(listResult.stdout.includes('playwright-generate-test (explicit:false)'),
'Explicitly disabled item should show explicit reason');
});

View File

@ -119,14 +119,14 @@ async function runTests() {
// Check it's explicitly disabled
const listResult1 = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
assert(listResult1.stdout.includes('create-readme (explicit)'), 'Should show explicit disabled');
assert(listResult1.stdout.includes('create-readme (explicit:false)'), 'Should show explicit disabled');
// Force enable all with --all flag
await runCommand(`node awesome-copilot.js toggle prompts all on --all --config ${TEST_CONFIG}`);
// Check it's now explicitly enabled
const listResult2 = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
assert(listResult2.stdout.includes('create-readme (explicit)') && listResult2.stdout.includes('[✓]'), 'Should be explicitly enabled');
assert(listResult2.stdout.includes('create-readme (explicit:true)') && listResult2.stdout.includes('[✓]'), 'Should be explicitly enabled');
});
// Test 4: File cleanup on disable
@ -180,7 +180,7 @@ async function runTests() {
// Double-check with list command
const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
assert(listResult.stdout.includes('playwright-generate-test (explicit)'), "'playwright-generate-test (explicit)' should be present in the list output");
assert(listResult.stdout.includes('playwright-generate-test (explicit:false)'), "'playwright-generate-test (explicit:false)' should be present in the list output");
assert(!listResult.stdout.includes('[✓] playwright-generate-test'), "'[✓] playwright-generate-test' should NOT be present in the list output (should remain explicitly disabled)");
});