aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
143 lines (123 loc) • 3.77 kB
JavaScript
;
const fs = require('fs');
const path = require('path');
const FORBIDDEN_ABSOLUTE_PATTERNS = [
/\/Users\/[^\s/'"]+/g,
/\/home\/[^\s/'"]+/g,
/[A-Za-z]:\\Users\\[^\s\\'"]+/g,
];
function getDefaultOptions() {
const projectRoot = process.cwd();
return {
projectRoot,
skillsDir: path.join(projectRoot, '.codex', 'skills'),
requiredFiles: [
path.join(projectRoot, 'AGENTS.md'),
path.join(projectRoot, '.aios-core', 'product', 'templates', 'ide-rules', 'codex-rules.md'),
],
quiet: false,
json: false,
};
}
function parseArgs(argv = process.argv.slice(2)) {
const args = new Set(argv);
return {
quiet: args.has('--quiet') || args.has('-q'),
json: args.has('--json'),
};
}
function listSkillFiles(skillsDir) {
if (!fs.existsSync(skillsDir)) return [];
return fs.readdirSync(skillsDir, { withFileTypes: true })
.filter(entry => entry.isDirectory() && entry.name.startsWith('aios-'))
.map(entry => path.join(skillsDir, entry.name, 'SKILL.md'))
.filter(file => fs.existsSync(file));
}
function collectAbsolutePathViolations(content, filePath) {
const errors = [];
const lines = content.split('\n');
lines.forEach((line, index) => {
for (const pattern of FORBIDDEN_ABSOLUTE_PATTERNS) {
const matches = line.match(pattern) || [];
for (const match of matches) {
errors.push(`${filePath}:${index + 1} forbidden absolute path "${match}"`);
}
}
});
return errors;
}
function validateSkillPathConventions(content, filePath) {
const errors = [];
if (!content.includes('.aios-core/development/agents/')) {
errors.push(`${filePath} missing canonical source path ".aios-core/development/agents/"`);
}
if (!content.includes('.aios-core/development/scripts/generate-greeting.js')) {
errors.push(`${filePath} missing canonical greeting script path`);
}
return errors;
}
function validatePaths(options = {}) {
const resolved = { ...getDefaultOptions(), ...options };
const errors = [];
const checkedFiles = [];
const skillFiles = listSkillFiles(resolved.skillsDir);
const filesToCheck = [...resolved.requiredFiles, ...skillFiles];
for (const file of filesToCheck) {
if (!fs.existsSync(file)) {
errors.push(`Missing required file: ${path.relative(resolved.projectRoot, file)}`);
continue;
}
let content;
try {
content = fs.readFileSync(file, 'utf8');
} catch (error) {
errors.push(`Unable to read file: ${path.relative(resolved.projectRoot, file)} (${error.message})`);
continue;
}
checkedFiles.push(file);
errors.push(...collectAbsolutePathViolations(content, path.relative(resolved.projectRoot, file)));
if (file.endsWith('SKILL.md')) {
errors.push(...validateSkillPathConventions(content, path.relative(resolved.projectRoot, file)));
}
}
return {
ok: errors.length === 0,
checked: checkedFiles.length,
errors,
};
}
function formatHumanReport(result) {
if (result.ok) {
return `✅ Path validation passed (${result.checked} files checked)`;
}
return [
`❌ Path validation failed (${result.errors.length} issue(s))`,
...result.errors.map(error => `- ${error}`),
].join('\n');
}
function main() {
const args = parseArgs();
const result = validatePaths(args);
if (!args.quiet) {
if (args.json) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(formatHumanReport(result));
}
}
if (!result.ok) {
process.exitCode = 1;
}
}
if (require.main === module) {
main();
}
module.exports = {
validatePaths,
parseArgs,
getDefaultOptions,
listSkillFiles,
collectAbsolutePathViolations,
validateSkillPathConventions,
};