fix: Prevent duplicate manifest entries and update GitHub Copilot tool names

- Deduplicate module lists in manifest generator using Set to prevent duplicate entries in installed manifests
- Update GitHub Copilot tool names to match official VS Code documentation (November 2025)
- Clean up legacy bmad/, bmd/, and web-bundles directories
This commit is contained in:
Brian Madison
2025-11-08 01:06:09 -06:00
parent 61955e8e96
commit a4bbfc4b6e
136 changed files with 76 additions and 42785 deletions

View File

@@ -34,8 +34,9 @@ class ManifestGenerator {
// Store modules list (all modules including preserved ones)
const preservedModules = options.preservedModules || [];
this.modules = ['core', ...selectedModules, ...preservedModules];
this.updatedModules = ['core', ...selectedModules]; // Only these get rescanned
// Deduplicate modules list to prevent duplicates
this.modules = [...new Set(['core', ...selectedModules, ...preservedModules])];
this.updatedModules = [...new Set(['core', ...selectedModules])]; // Only these get rescanned
this.preservedModules = preservedModules; // These stay as-is in CSVs
this.bmadDir = bmadDir;
this.allInstalledFiles = installedFiles;
@@ -91,15 +92,8 @@ class ManifestGenerator {
async collectWorkflows(selectedModules) {
this.workflows = [];
// Get core workflows from installed bmad directory
const corePath = path.join(this.bmadDir, 'core');
if (await fs.pathExists(corePath)) {
const coreWorkflows = await this.getWorkflowsFromPath(corePath, 'core');
this.workflows.push(...coreWorkflows);
}
// Get module workflows from installed bmad directory
for (const moduleName of selectedModules) {
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const modulePath = path.join(this.bmadDir, moduleName);
if (await fs.pathExists(modulePath)) {
@@ -186,15 +180,8 @@ class ManifestGenerator {
async collectAgents(selectedModules) {
this.agents = [];
// Get core agents from installed bmad directory
const coreAgentsPath = path.join(this.bmadDir, 'core', 'agents');
if (await fs.pathExists(coreAgentsPath)) {
const coreAgents = await this.getAgentsFromDir(coreAgentsPath, 'core');
this.agents.push(...coreAgents);
}
// Get module agents from installed bmad directory
for (const moduleName of selectedModules) {
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const agentsPath = path.join(this.bmadDir, moduleName, 'agents');
if (await fs.pathExists(agentsPath)) {
@@ -300,15 +287,8 @@ class ManifestGenerator {
async collectTasks(selectedModules) {
this.tasks = [];
// Get core tasks from installed bmad directory
const coreTasksPath = path.join(this.bmadDir, 'core', 'tasks');
if (await fs.pathExists(coreTasksPath)) {
const coreTasks = await this.getTasksFromDir(coreTasksPath, 'core');
this.tasks.push(...coreTasks);
}
// Get module tasks from installed bmad directory
for (const moduleName of selectedModules) {
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const tasksPath = path.join(this.bmadDir, moduleName, 'tasks');
if (await fs.pathExists(tasksPath)) {
@@ -376,15 +356,8 @@ class ManifestGenerator {
async collectTools(selectedModules) {
this.tools = [];
// Get core tools from installed bmad directory
const coreToolsPath = path.join(this.bmadDir, 'core', 'tools');
if (await fs.pathExists(coreToolsPath)) {
const coreTools = await this.getToolsFromDir(coreToolsPath, 'core');
this.tools.push(...coreTools);
}
// Get module tools from installed bmad directory
for (const moduleName of selectedModules) {
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const toolsPath = path.join(this.bmadDir, moduleName, 'tools');
if (await fs.pathExists(toolsPath)) {
@@ -564,26 +537,14 @@ class ManifestGenerator {
async writeWorkflowManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'workflow-manifest.csv');
// Define expected columns and defaults for schema upgrade
const expectedColumns = ['name', 'description', 'module', 'path', 'standalone'];
const defaultValues = { standalone: 'false' };
// Get preserved rows from existing CSV (module is column 2, 0-indexed)
const preservedRows = await this.getPreservedCsvRows(csvPath, 2, expectedColumns, defaultValues);
// Create CSV header with standalone column
let csv = 'name,description,module,path,standalone\n';
// Add new rows for updated modules
// Add all workflows
for (const workflow of this.workflows) {
csv += `"${workflow.name}","${workflow.description}","${workflow.module}","${workflow.path}","${workflow.standalone}"\n`;
}
// Add preserved rows for modules we didn't update
for (const row of preservedRows) {
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
@@ -595,36 +556,14 @@ class ManifestGenerator {
async writeAgentManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'agent-manifest.csv');
// Define expected columns (no schema changes for agents currently)
const expectedColumns = [
'name',
'displayName',
'title',
'icon',
'role',
'identity',
'communicationStyle',
'principles',
'module',
'path',
];
// Get preserved rows from existing CSV (module is column 8, 0-indexed)
const preservedRows = await this.getPreservedCsvRows(csvPath, 8, expectedColumns);
// Create CSV header with persona fields
let csv = 'name,displayName,title,icon,role,identity,communicationStyle,principles,module,path\n';
// Add new rows for updated modules
// Add all agents
for (const agent of this.agents) {
csv += `"${agent.name}","${agent.displayName}","${agent.title}","${agent.icon}","${agent.role}","${agent.identity}","${agent.communicationStyle}","${agent.principles}","${agent.module}","${agent.path}"\n`;
}
// Add preserved rows for modules we didn't update
for (const row of preservedRows) {
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
@@ -636,26 +575,14 @@ class ManifestGenerator {
async writeTaskManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'task-manifest.csv');
// Define expected columns and defaults for schema upgrade
const expectedColumns = ['name', 'displayName', 'description', 'module', 'path', 'standalone'];
const defaultValues = { standalone: 'false' };
// Get preserved rows from existing CSV (module is column 3, 0-indexed)
const preservedRows = await this.getPreservedCsvRows(csvPath, 3, expectedColumns, defaultValues);
// Create CSV header with standalone column
let csv = 'name,displayName,description,module,path,standalone\n';
// Add new rows for updated modules
// Add all tasks
for (const task of this.tasks) {
csv += `"${task.name}","${task.displayName}","${task.description}","${task.module}","${task.path}","${task.standalone}"\n`;
}
// Add preserved rows for modules we didn't update
for (const row of preservedRows) {
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
@@ -667,26 +594,14 @@ class ManifestGenerator {
async writeToolManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'tool-manifest.csv');
// Define expected columns and defaults for schema upgrade
const expectedColumns = ['name', 'displayName', 'description', 'module', 'path', 'standalone'];
const defaultValues = { standalone: 'false' };
// Get preserved rows from existing CSV (module is column 3, 0-indexed)
const preservedRows = await this.getPreservedCsvRows(csvPath, 3, expectedColumns, defaultValues);
// Create CSV header with standalone column
let csv = 'name,displayName,description,module,path,standalone\n';
// Add new rows for updated modules
// Add all tools
for (const tool of this.tools) {
csv += `"${tool.name}","${tool.displayName}","${tool.description}","${tool.module}","${tool.path}","${tool.standalone}"\n`;
}
// Add preserved rows for modules we didn't update
for (const row of preservedRows) {
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
@@ -714,9 +629,6 @@ class ManifestGenerator {
async writeFilesManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'files-manifest.csv');
// Get preserved rows from existing CSV (module is column 2, 0-indexed)
const preservedRows = await this.getPreservedCsvRows(csvPath, 2);
// Create CSV header with hash column
let csv = 'type,name,module,path,hash\n';
@@ -763,16 +675,11 @@ class ManifestGenerator {
return a.name.localeCompare(b.name);
});
// Add rows for updated modules
// Add all files
for (const file of allFiles) {
csv += `"${file.type}","${file.name}","${file.module}","${file.path}","${file.hash}"\n`;
}
// Add preserved rows for modules we didn't update
for (const row of preservedRows) {
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}

View File

@@ -214,24 +214,31 @@ class GitHubCopilotSetup extends BaseIdeSetup {
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
const description = whenToUseMatch ? whenToUseMatch[1] : `Activates the ${title} agent persona.`;
// Available GitHub Copilot tools
// Available GitHub Copilot tools (November 2025 - Official VS Code Documentation)
// Reference: https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools
const tools = [
'changes',
'codebase',
'fetch',
'findTestFiles',
'githubRepo',
'problems',
'usages',
'editFiles',
'runCommands',
'runTasks',
'runTests',
'search',
'searchResults',
'terminalLastCommand',
'terminalSelection',
'testFailure',
'changes', // List of source control changes
'codebase', // Perform code search in workspace
'createDirectory', // Create new directory in workspace
'createFile', // Create new file in workspace
'editFiles', // Apply edits to files in workspace
'fetch', // Fetch content from web page
'fileSearch', // Search files using glob patterns
'githubRepo', // Perform code search in GitHub repo
'listDirectory', // List files in a directory
'problems', // Add workspace issues from Problems panel
'readFile', // Read content of a file in workspace
'runInTerminal', // Run shell command in integrated terminal
'runTask', // Run existing task in workspace
'runTests', // Run unit tests in workspace
'runVscodeCommand', // Run VS Code command
'search', // Enable file searching in workspace
'searchResults', // Get search results from Search view
'terminalLastCommand', // Get last terminal command and output
'terminalSelection', // Get current terminal selection
'testFailure', // Get unit test failure information
'textSearch', // Find text in files
'usages', // Find references and navigate definitions
];
let chatmodeContent = `---