UNPKG

tops-bmad

Version:

CLI tool to install BMAD workflow files into any project with integrated Shai-Hulud 2.0 security scanning

129 lines (111 loc) 4.63 kB
import fs from "fs-extra"; import path from "path"; import yaml from "js-yaml"; /** * Creates or updates the project configuration file * @param {Object} config - Configuration object with project details * @returns {Promise<void>} */ export async function saveProjectConfig(config) { try { // Ensure .bmad/bmm directory exists const configDir = path.join(process.cwd(), ".bmad", "bmm"); await fs.ensureDir(configDir); const configPath = path.join(configDir, "config.yaml"); // Create project config object const projectConfig = { project: { name: config.projectName || "", applicationType: config.applicationType || "", deviceSupport: config.deviceSupport || [], multiTenancy: config.multiTenancy || false, roleBasedAccess: config.roleBasedAccess || false, backgroundCrons: config.backgroundCrons || false, thirdPartyTools: config.thirdPartyTools || [] } }; // Check if config file already exists if (await fs.pathExists(configPath)) { // Read existing config as text to preserve comments and formatting const existingContent = await fs.readFile(configPath, "utf8"); // Check if project section already exists in the file (as a top-level key) // Look for "project:" at the start of a line (possibly with whitespace before it) const hasProjectSection = /^project:\s*$/m.test(existingContent); if (hasProjectSection) { // Project section exists, replace it while preserving everything else const projectYaml = yaml.dump(projectConfig, { indent: 2, lineWidth: -1, noRefs: true }); // Find the project section and replace everything from "project:" to end of file // This regex matches from "project:" to end of file const replaceRegex = /^project:[\s\S]*$/m; const lines = existingContent.split('\n'); let projectStartIndex = -1; // Find the line with "project:" for (let i = 0; i < lines.length; i++) { if (/^\s*project:\s*$/.test(lines[i])) { projectStartIndex = i; break; } } if (projectStartIndex >= 0) { // Replace from project: line to end of file const beforeProject = lines.slice(0, projectStartIndex).join('\n'); const updatedContent = beforeProject + '\n\n' + projectYaml.trim(); await fs.writeFile(configPath, updatedContent, "utf8"); console.log(`✅ Project configuration updated in ${configPath}`); } else { // Fallback: append if we can't find the exact location const separator = existingContent.trim().endsWith('\n') ? '\n\n' : '\n\n'; const updatedContent = existingContent.trim() + separator + projectYaml.trim(); await fs.writeFile(configPath, updatedContent, "utf8"); console.log(`✅ Project configuration appended to ${configPath}`); } } else { // Project section doesn't exist, append it preserving all existing content const projectYaml = yaml.dump(projectConfig, { indent: 2, lineWidth: -1, noRefs: true }); // Add newline separator const separator = existingContent.trim().endsWith('\n') ? '\n\n' : '\n\n'; const updatedContent = existingContent.trim() + separator + projectYaml; await fs.writeFile(configPath, updatedContent, "utf8"); console.log(`✅ Project configuration appended to ${configPath}`); } } else { // File doesn't exist, create new one const yamlContent = yaml.dump(projectConfig, { indent: 2, lineWidth: -1, noRefs: true }); await fs.writeFile(configPath, yamlContent, "utf8"); console.log(`✅ Configuration saved to ${configPath}`); } } catch (error) { console.error("❌ Error saving project configuration:", error.message); throw error; } } /** * Reads the project configuration file * @returns {Promise<Object|null>} */ export async function loadProjectConfig() { try { const configPath = path.join(process.cwd(), ".bmad", "bmm", "config.yaml"); if (!(await fs.pathExists(configPath))) { return null; } const yamlContent = await fs.readFile(configPath, "utf8"); const config = yaml.load(yamlContent); return config; } catch (error) { console.warn("⚠️ Warning: Could not load project configuration:", error.message); return null; } }