chore: add code formatting config and pre-commit hooks (#450)

This commit is contained in:
manjaroblack
2025-08-16 19:08:39 -05:00
committed by GitHub
parent 51284d6ecf
commit ed539432fb
130 changed files with 11886 additions and 10939 deletions

View File

@@ -1,32 +1,24 @@
const fs = require("fs-extra");
const path = require("path");
const crypto = require("crypto");
const yaml = require("js-yaml");
const chalk = require("chalk").default || require("chalk");
const { createReadStream, createWriteStream, promises: fsPromises } = require('fs');
const { pipeline } = require('stream/promises');
const fs = require('fs-extra');
const path = require('node:path');
const crypto = require('node:crypto');
const yaml = require('js-yaml');
const chalk = require('chalk');
const { createReadStream, createWriteStream, promises: fsPromises } = require('node:fs');
const { pipeline } = require('node:stream/promises');
const resourceLocator = require('./resource-locator');
class FileManager {
constructor() {
this.manifestDir = ".bmad-core";
this.manifestFile = "install-manifest.yaml";
}
constructor() {}
async copyFile(source, destination) {
try {
await fs.ensureDir(path.dirname(destination));
// Use streaming for large files (> 10MB)
const stats = await fs.stat(source);
if (stats.size > 10 * 1024 * 1024) {
await pipeline(
createReadStream(source),
createWriteStream(destination)
);
} else {
await fs.copy(source, destination);
}
await (stats.size > 10 * 1024 * 1024
? pipeline(createReadStream(source), createWriteStream(destination))
: fs.copy(source, destination));
return true;
} catch (error) {
console.error(chalk.red(`Failed to copy ${source}:`), error.message);
@@ -37,32 +29,24 @@ class FileManager {
async copyDirectory(source, destination) {
try {
await fs.ensureDir(destination);
// Use streaming copy for large directories
const files = await resourceLocator.findFiles('**/*', {
cwd: source,
nodir: true
nodir: true,
});
// Process files in batches to avoid memory issues
const batchSize = 50;
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize);
for (let index = 0; index < files.length; index += batchSize) {
const batch = files.slice(index, index + batchSize);
await Promise.all(
batch.map(file =>
this.copyFile(
path.join(source, file),
path.join(destination, file)
)
)
batch.map((file) => this.copyFile(path.join(source, file), path.join(destination, file))),
);
}
return true;
} catch (error) {
console.error(
chalk.red(`Failed to copy directory ${source}:`),
error.message
);
console.error(chalk.red(`Failed to copy directory ${source}:`), error.message);
return false;
}
}
@@ -73,17 +57,16 @@ class FileManager {
for (const file of files) {
const sourcePath = path.join(sourceDir, file);
const destPath = path.join(destDir, file);
const destinationPath = path.join(destDir, file);
// Use root replacement if rootValue is provided and file needs it
const needsRootReplacement = rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
const needsRootReplacement =
rootValue && (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'));
let success = false;
if (needsRootReplacement) {
success = await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue);
} else {
success = await this.copyFile(sourcePath, destPath);
}
success = await (needsRootReplacement
? this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)
: this.copyFile(sourcePath, destinationPath));
if (success) {
copied.push(file);
@@ -97,32 +80,28 @@ class FileManager {
try {
// Use streaming for hash calculation to reduce memory usage
const stream = createReadStream(filePath);
const hash = crypto.createHash("sha256");
const hash = crypto.createHash('sha256');
for await (const chunk of stream) {
hash.update(chunk);
}
return hash.digest("hex").slice(0, 16);
} catch (error) {
return hash.digest('hex').slice(0, 16);
} catch {
return null;
}
}
async createManifest(installDir, config, files) {
const manifestPath = path.join(
installDir,
this.manifestDir,
this.manifestFile
);
const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
// Read version from package.json
let coreVersion = "unknown";
let coreVersion = 'unknown';
try {
const packagePath = path.join(__dirname, '..', '..', '..', 'package.json');
const packageJson = require(packagePath);
coreVersion = packageJson.version;
} catch (error) {
} catch {
console.warn("Could not read version from package.json, using 'unknown'");
}
@@ -156,31 +135,23 @@ class FileManager {
}
async readManifest(installDir) {
const manifestPath = path.join(
installDir,
this.manifestDir,
this.manifestFile
);
const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile);
try {
const content = await fs.readFile(manifestPath, "utf8");
const content = await fs.readFile(manifestPath, 'utf8');
return yaml.load(content);
} catch (error) {
} catch {
return null;
}
}
async readExpansionPackManifest(installDir, packId) {
const manifestPath = path.join(
installDir,
`.${packId}`,
this.manifestFile
);
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
try {
const content = await fs.readFile(manifestPath, "utf8");
const content = await fs.readFile(manifestPath, 'utf8');
return yaml.load(content);
} catch (error) {
} catch {
return null;
}
}
@@ -203,24 +174,24 @@ class FileManager {
async checkFileIntegrity(installDir, manifest) {
const result = {
missing: [],
modified: []
modified: [],
};
for (const file of manifest.files) {
const filePath = path.join(installDir, file.path);
// Skip checking the manifest file itself - it will always be different due to timestamps
if (file.path.endsWith('install-manifest.yaml')) {
continue;
}
if (!(await this.pathExists(filePath))) {
result.missing.push(file.path);
} else {
if (await this.pathExists(filePath)) {
const currentHash = await this.calculateFileHash(filePath);
if (currentHash && currentHash !== file.hash) {
result.modified.push(file.path);
}
} else {
result.missing.push(file.path);
}
}
@@ -228,7 +199,7 @@ class FileManager {
}
async backupFile(filePath) {
const backupPath = filePath + ".bak";
const backupPath = filePath + '.bak';
let counter = 1;
let finalBackupPath = backupPath;
@@ -256,7 +227,7 @@ class FileManager {
}
async readFile(filePath) {
return fs.readFile(filePath, "utf8");
return fs.readFile(filePath, 'utf8');
}
async writeFile(filePath, content) {
@@ -269,14 +240,10 @@ class FileManager {
}
async createExpansionPackManifest(installDir, packId, config, files) {
const manifestPath = path.join(
installDir,
`.${packId}`,
this.manifestFile
);
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
const manifest = {
version: config.expansionPackVersion || require("../../../package.json").version,
version: config.expansionPackVersion || require('../../../package.json').version,
installed_at: new Date().toISOString(),
install_type: config.installType,
expansion_pack_id: config.expansionPackId,
@@ -306,24 +273,24 @@ class FileManager {
async modifyCoreConfig(installDir, config) {
const coreConfigPath = path.join(installDir, '.bmad-core', 'core-config.yaml');
try {
// Read the existing core-config.yaml
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
const coreConfig = yaml.load(coreConfigContent);
// Modify sharding settings if provided
if (config.prdSharded !== undefined) {
coreConfig.prd.prdSharded = config.prdSharded;
}
if (config.architectureSharded !== undefined) {
coreConfig.architecture.architectureSharded = config.architectureSharded;
}
// Write back the modified config
await fs.writeFile(coreConfigPath, yaml.dump(coreConfig, { indent: 2 }));
return true;
} catch (error) {
console.error(chalk.red(`Failed to modify core-config.yaml:`), error.message);
@@ -335,31 +302,32 @@ class FileManager {
try {
// Check file size to determine if we should stream
const stats = await fs.stat(source);
if (stats.size > 5 * 1024 * 1024) { // 5MB threshold
if (stats.size > 5 * 1024 * 1024) {
// 5MB threshold
// Use streaming for large files
const { Transform } = require('stream');
const { Transform } = require('node:stream');
const replaceStream = new Transform({
transform(chunk, encoding, callback) {
const modified = chunk.toString().replace(/\{root\}/g, rootValue);
const modified = chunk.toString().replaceAll('{root}', rootValue);
callback(null, modified);
}
},
});
await this.ensureDirectory(path.dirname(destination));
await pipeline(
createReadStream(source, { encoding: 'utf8' }),
replaceStream,
createWriteStream(destination, { encoding: 'utf8' })
createWriteStream(destination, { encoding: 'utf8' }),
);
} else {
// Regular approach for smaller files
const content = await fsPromises.readFile(source, 'utf8');
const updatedContent = content.replace(/\{root\}/g, rootValue);
const updatedContent = content.replaceAll('{root}', rootValue);
await this.ensureDirectory(path.dirname(destination));
await fsPromises.writeFile(destination, updatedContent, 'utf8');
}
return true;
} catch (error) {
console.error(chalk.red(`Failed to copy ${source} with root replacement:`), error.message);
@@ -367,45 +335,55 @@ class FileManager {
}
}
async copyDirectoryWithRootReplacement(source, destination, rootValue, fileExtensions = ['.md', '.yaml', '.yml']) {
async copyDirectoryWithRootReplacement(
source,
destination,
rootValue,
fileExtensions = ['.md', '.yaml', '.yml'],
) {
try {
await this.ensureDirectory(destination);
// Get all files in source directory
const files = await resourceLocator.findFiles('**/*', {
cwd: source,
nodir: true
const files = await resourceLocator.findFiles('**/*', {
cwd: source,
nodir: true,
});
let replacedCount = 0;
for (const file of files) {
const sourcePath = path.join(source, file);
const destPath = path.join(destination, file);
const destinationPath = path.join(destination, file);
// Check if this file type should have {root} replacement
const shouldReplace = fileExtensions.some(ext => file.endsWith(ext));
const shouldReplace = fileExtensions.some((extension) => file.endsWith(extension));
if (shouldReplace) {
if (await this.copyFileWithRootReplacement(sourcePath, destPath, rootValue)) {
if (await this.copyFileWithRootReplacement(sourcePath, destinationPath, rootValue)) {
replacedCount++;
}
} else {
// Regular copy for files that don't need replacement
await this.copyFile(sourcePath, destPath);
await this.copyFile(sourcePath, destinationPath);
}
}
if (replacedCount > 0) {
console.log(chalk.dim(` Processed ${replacedCount} files with {root} replacement`));
}
return true;
} catch (error) {
console.error(chalk.red(`Failed to copy directory ${source} with root replacement:`), error.message);
console.error(
chalk.red(`Failed to copy directory ${source} with root replacement:`),
error.message,
);
return false;
}
}
manifestDir = '.bmad-core';
manifestFile = 'install-manifest.yaml';
}
module.exports = new FileManager();