fix: ensure POSIX-compliant newlines in generated files

- Add final newline check to YAML config generation
- Add final newline check to YAML manifest generation
- Add final newline check to agent .md file generation
- Ensures all text files end with \n per POSIX standard
- Fixes 'No newline at end of file' git warnings
This commit is contained in:
Serhii 2025-11-04 19:10:28 +02:00
parent ccd6cacd89
commit c32afa0260
No known key found for this signature in database
GPG Key ID: 84A22AF415BE7704
6 changed files with 37 additions and 15 deletions

View File

@ -68,7 +68,9 @@ class IdeConfigManager {
sortKeys: false, sortKeys: false,
}); });
await fs.writeFile(configPath, yamlContent, 'utf8'); // Ensure POSIX-compliant final newline
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
await fs.writeFile(manifestPath, content, 'utf8');
} }
/** /**

View File

@ -906,8 +906,9 @@ class Installer {
} }
} }
// Write the clean config file // Write the clean config file with POSIX-compliant final newline
await fs.writeFile(configPath, header + yamlContent, 'utf8'); const content = header + yamlContent;
await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
// Track the config file in installedFiles // Track the config file in installedFiles
this.installedFiles.push(configPath); this.installedFiles.push(configPath);
@ -1195,8 +1196,9 @@ class Installer {
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Write the built .md file to bmad/{module}/agents/ // Write the built .md file to bmad/{module}/agents/ with POSIX-compliant final newline
await fs.writeFile(mdPath, xmlContent, 'utf8'); const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(mdPath, content, 'utf8');
this.installedFiles.push(mdPath); this.installedFiles.push(mdPath);
// Remove the source YAML file - we can regenerate from installer source if needed // Remove the source YAML file - we can regenerate from installer source if needed
@ -1213,7 +1215,9 @@ class Installer {
if (content.includes('<agent') && !content.includes('<activation')) { if (content.includes('<agent') && !content.includes('<activation')) {
// Inject the activation block using XML handler // Inject the activation block using XML handler
content = this.xmlHandler.injectActivationSimple(content); content = this.xmlHandler.injectActivationSimple(content);
await fs.writeFile(agentPath, content, 'utf8'); // Ensure POSIX-compliant final newline
const finalContent = content.endsWith('\n') ? content : content + '\n';
await fs.writeFile(agentPath, finalContent, 'utf8');
} }
} }
} }
@ -1294,8 +1298,9 @@ class Installer {
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Write the built .md file // Write the built .md file with POSIX-compliant final newline
await fs.writeFile(targetMdPath, xmlContent, 'utf8'); const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(targetMdPath, content, 'utf8');
// Display result // Display result
if (customizedFields.length > 0) { if (customizedFields.length > 0) {
@ -1387,8 +1392,9 @@ class Installer {
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime // DO NOT replace {project-root} - LLMs understand this placeholder at runtime
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir); // const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
// Write the rebuilt .md file // Write the rebuilt .md file with POSIX-compliant final newline
await fs.writeFile(targetMdPath, xmlContent, 'utf8'); const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
await fs.writeFile(targetMdPath, content, 'utf8');
// Display result with customizations if any // Display result with customizations if any
if (customizedFields.length > 0) { if (customizedFields.length > 0) {
@ -2005,6 +2011,11 @@ class Installer {
configContent += processedTemplate; configContent += processedTemplate;
// Ensure POSIX-compliant final newline
if (!configContent.endsWith('\n')) {
configContent += '\n';
}
await fs.writeFile(configPath, configContent, 'utf8'); await fs.writeFile(configPath, configContent, 'utf8');
this.installedFiles.push(configPath); // Track agent config files this.installedFiles.push(configPath); // Track agent config files
createdCount++; createdCount++;

View File

@ -469,7 +469,9 @@ class ManifestGenerator {
sortKeys: false, sortKeys: false,
}); });
await fs.writeFile(manifestPath, yamlStr); // Ensure POSIX-compliant final newline
const content = yamlStr.endsWith('\n') ? yamlStr : yamlStr + '\n';
await fs.writeFile(manifestPath, content);
return manifestPath; return manifestPath;
} }

View File

@ -35,7 +35,9 @@ class Manifest {
sortKeys: false, sortKeys: false,
}); });
await fs.writeFile(manifestPath, yamlContent, 'utf8'); // Ensure POSIX-compliant final newline
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
await fs.writeFile(manifestPath, content, 'utf8');
return { success: true, path: manifestPath, filesTracked: 0 }; return { success: true, path: manifestPath, filesTracked: 0 };
} }
@ -104,7 +106,9 @@ class Manifest {
sortKeys: false, sortKeys: false,
}); });
await fs.writeFile(manifestPath, yamlContent, 'utf8'); // Ensure POSIX-compliant final newline
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
await fs.writeFile(manifestPath, content, 'utf8');
return manifest; return manifest;
} }

View File

@ -33,7 +33,9 @@ class Config {
}); });
await fs.ensureDir(path.dirname(configPath)); await fs.ensureDir(path.dirname(configPath));
await fs.writeFile(configPath, yamlContent, 'utf8'); // Ensure POSIX-compliant final newline
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
await fs.writeFile(configPath, content, 'utf8');
} }
/** /**

View File

@ -64,7 +64,8 @@ async function formatYamlContent(content, filename) {
noRefs: true, noRefs: true,
sortKeys: false, // Preserve key order sortKeys: false, // Preserve key order
}); });
return formatted; // Ensure POSIX-compliant final newline
return formatted.endsWith('\n') ? formatted : formatted + '\n';
} catch (error) { } catch (error) {
console.error(chalk.red(`❌ YAML syntax error in ${filename}:`), error.message); console.error(chalk.red(`❌ YAML syntax error in ${filename}:`), error.message);
console.error(chalk.yellow(`💡 Try manually fixing the YAML structure first`)); console.error(chalk.yellow(`💡 Try manually fixing the YAML structure first`));