BMAD-METHOD/BETA-V3/build-bmad-assets.js

186 lines
6.1 KiB
JavaScript

#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
// --- Configuration ---
const DEFAULT_ASSET_FOLDER_ROOT = "./bmad-agent";
const SUBDIRECTORIES_TO_PROCESS = {
checklists: "checklists.txt",
data: "data.txt",
personas: "personas.txt",
tasks: "tasks.txt",
templates: "templates.txt",
};
const BUILD_DIR_NAME = "build";
// --- Helper Functions ---
function getBaseFilename(filePath) {
// Removes the last extension, e.g., "file.sample.txt" -> "file.sample"
return path.basename(filePath, path.extname(filePath));
}
function ensureDirectoryExists(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
// --- Main Script Logic ---
async function main() {
// 1. Determine and validate asset folder root
const providedAssetRoot = process.argv[2]; // Get the first argument after script name
const assetFolderRootInput = providedAssetRoot || DEFAULT_ASSET_FOLDER_ROOT;
console.log(`Initial asset folder root: ${assetFolderRootInput}`);
let assetFolderRoot;
try {
assetFolderRoot = path.resolve(assetFolderRootInput);
if (
!fs.existsSync(assetFolderRoot) ||
!fs.statSync(assetFolderRoot).isDirectory()
) {
console.error(
`Error: Asset folder root '${assetFolderRootInput}' (resolved to '${assetFolderRoot}') not found or not a directory.`
);
process.exit(1);
}
} catch (error) {
console.error(
`Error: Could not resolve asset folder root '${assetFolderRootInput}'. ${error.message}`
);
process.exit(1);
}
console.log(`Using resolved asset folder root: ${assetFolderRoot}`);
const buildDir = path.join(assetFolderRoot, BUILD_DIR_NAME);
// 2. Perform pre-check for duplicate base filenames
console.log("\nPerforming pre-check for duplicate base filenames...");
for (const subdirName of Object.keys(SUBDIRECTORIES_TO_PROCESS)) {
const sourceSubdir = path.join(assetFolderRoot, subdirName);
if (
!fs.existsSync(sourceSubdir) ||
!fs.statSync(sourceSubdir).isDirectory()
) {
// This directory might not exist, which is fine for the pre-check.
// The main processing loop will handle warnings for missing/empty dirs.
continue;
}
try {
const files = fs.readdirSync(sourceSubdir);
if (files.length === 0) {
// Empty directory, no duplicates possible.
continue;
}
console.log(` Checking for duplicates in '${sourceSubdir}'...`);
const baseFilenamesSeen = {}; // Using an object as a hash map
for (const filenameWithExt of files) {
const filePath = path.join(sourceSubdir, filenameWithExt);
if (fs.statSync(filePath).isFile()) {
const baseName = getBaseFilename(filenameWithExt);
if (baseFilenamesSeen[baseName]) {
console.error(
`Error: Duplicate base name '${baseName}' found in directory '${sourceSubdir}'.`
);
console.error(
` Conflicting files: '${baseFilenamesSeen[baseName]}' and '${filenameWithExt}'.`
);
console.error(
` Please ensure all files in a subdirectory have unique names after removing their last extensions.`
);
process.exit(1);
} else {
baseFilenamesSeen[baseName] = filenameWithExt;
}
}
}
console.log(` No duplicates found in '${sourceSubdir}'.`);
} catch (error) {
console.warn(
`Warning: Could not read directory '${sourceSubdir}' during pre-check. ${error.message}`
);
// Continue to allow the main loop to handle it more formally.
}
}
console.log(
"Pre-check completed successfully. No duplicate base filenames found."
);
// 3. Create build directory
ensureDirectoryExists(buildDir);
console.log(`\nBuild directory is: ${buildDir}`);
// 4. Main processing loop
for (const [subdirName, outputFilename] of Object.entries(
SUBDIRECTORIES_TO_PROCESS
)) {
const sourceSubdir = path.join(assetFolderRoot, subdirName);
const targetFile = path.join(buildDir, outputFilename);
console.log(`Processing '${subdirName}' directory into '${targetFile}'`);
// Delete target file if it exists, then create it empty
if (fs.existsSync(targetFile)) {
fs.unlinkSync(targetFile);
}
fs.writeFileSync(targetFile, ""); // Create empty file
if (
!fs.existsSync(sourceSubdir) ||
!fs.statSync(sourceSubdir).isDirectory()
) {
console.warn(
`Warning: Source directory '${sourceSubdir}' not found. '${targetFile}' will remain empty.`
);
continue;
}
const files = fs.readdirSync(sourceSubdir);
if (files.length === 0) {
console.warn(
`Warning: Source directory '${sourceSubdir}' is empty. '${targetFile}' will remain empty.`
);
continue;
}
for (const filenameWithExt of files) {
const filePath = path.join(sourceSubdir, filenameWithExt);
if (fs.statSync(filePath).isFile()) {
const baseName = getBaseFilename(filenameWithExt);
console.log(
` Appending content from '${filenameWithExt}' (as '${baseName}') to '${targetFile}'`
);
const fileContent = fs.readFileSync(filePath, "utf8");
const startMarker = `==================== START: ${baseName} ====================\n`;
const endMarker = `\n==================== END: ${baseName} ====================\n\n\n`; // Two blank lines after
fs.appendFileSync(targetFile, startMarker);
fs.appendFileSync(targetFile, fileContent);
// Ensure a newline before the end marker if fileContent doesn't end with one
if (!fileContent.endsWith("\n")) {
fs.appendFileSync(targetFile, "\n");
}
fs.appendFileSync(targetFile, endMarker);
}
}
console.log(`Finished processing '${subdirName}'.`);
}
console.log(`\nScript finished. Output files are in ${buildDir}`);
console.log(
"To run this script: node BETA-V3/build-bmad-assets.js [path/to/asset/folder]"
);
}
main().catch((error) => {
console.error("An unexpected error occurred:", error);
process.exit(1);
});