aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
199 lines (177 loc) • 5.57 kB
JavaScript
/**
* @fileoverview Parser for Markdown files with AIOS-MANAGED sections
* @module merger/parsers/markdown-section-parser
*/
// Regex patterns for AIOS markers
const AIOS_START_MARKER = /^<!--\s*AIOS-MANAGED-START:\s*([a-zA-Z0-9_-]+)\s*-->$/;
const AIOS_END_MARKER = /^<!--\s*AIOS-MANAGED-END:\s*([a-zA-Z0-9_-]+)\s*-->$/;
const HEADER_PATTERN = /^(#{1,6})\s+(.+)$/;
/**
* Parsed section from markdown
* @typedef {Object} ParsedSection
* @property {string} id - Section identifier (slug or marker id)
* @property {string} [title] - Section title (from header)
* @property {number} [level] - Header level (1-6)
* @property {number} startLine - Start line number (0-indexed)
* @property {number} [endLine] - End line number (0-indexed)
* @property {boolean} managed - True if AIOS-MANAGED section
* @property {string[]} lines - Lines in this section (excluding markers)
*/
/**
* Result of parsing a markdown file
* @typedef {Object} ParsedMarkdownFile
* @property {ParsedSection[]} sections - All sections found
* @property {boolean} hasAiosMarkers - True if file has AIOS-MANAGED markers
* @property {string[]} preamble - Lines before first section
* @property {string[]} rawLines - Original lines
*/
/**
* Convert a string to a URL-friendly slug
* @param {string} text - Text to slugify
* @returns {string} Slugified text
*/
function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '') // Remove special chars
.replace(/[\s_-]+/g, '-') // Replace spaces/underscores with hyphens
.replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
}
/**
* Parse a markdown file, identifying sections and AIOS-MANAGED areas
* @param {string} content - Markdown content
* @returns {ParsedMarkdownFile} Parsed result
*/
function parseMarkdownSections(content) {
if (!content || content.trim() === '') {
return {
sections: [],
hasAiosMarkers: false,
preamble: [],
rawLines: [],
};
}
const lines = content.split('\n');
const result = {
sections: [],
hasAiosMarkers: false,
preamble: [],
rawLines: lines,
};
let currentSection = null;
let aiosSection = null;
let inPreamble = true;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
// Check for AIOS start marker
const startMatch = trimmed.match(AIOS_START_MARKER);
if (startMatch) {
// Close any current non-managed section
if (currentSection && !currentSection.managed) {
currentSection.endLine = i - 1;
result.sections.push(currentSection);
currentSection = null;
}
// Start new AIOS-managed section
aiosSection = {
id: startMatch[1],
startLine: i,
managed: true,
lines: [],
};
result.hasAiosMarkers = true;
inPreamble = false;
continue;
}
// Check for AIOS end marker
const endMatch = trimmed.match(AIOS_END_MARKER);
if (endMatch && aiosSection) {
if (endMatch[1] === aiosSection.id) {
aiosSection.endLine = i;
result.sections.push(aiosSection);
aiosSection = null;
}
continue;
}
// If we're in an AIOS section, collect lines
if (aiosSection) {
aiosSection.lines.push(line);
continue;
}
// Check for regular header
const headerMatch = line.match(HEADER_PATTERN);
if (headerMatch) {
// Close any current section
if (currentSection) {
currentSection.endLine = i - 1;
result.sections.push(currentSection);
}
// Start new section
currentSection = {
id: slugify(headerMatch[2]),
title: headerMatch[2],
level: headerMatch[1].length,
startLine: i,
managed: false,
lines: [line],
};
inPreamble = false;
continue;
}
// Regular content line
if (inPreamble) {
result.preamble.push(line);
} else if (currentSection) {
currentSection.lines.push(line);
} else if (!aiosSection) {
// Content after an AIOS section but before next section
// This shouldn't happen in well-formed files, but handle it
result.preamble.push(line);
}
}
// Close final section if open
if (currentSection) {
currentSection.endLine = lines.length - 1;
result.sections.push(currentSection);
}
// Handle unclosed AIOS section (malformed)
if (aiosSection) {
aiosSection.endLine = lines.length - 1;
aiosSection.lines.push('<!-- WARNING: Unclosed AIOS-MANAGED section -->');
result.sections.push(aiosSection);
}
return result;
}
/**
* Check if content has AIOS-MANAGED markers
* @param {string} content - Markdown content
* @returns {boolean} True if markers found
*/
function hasAiosMarkers(content) {
if (!content) return false;
// Check for both START and END markers
const hasStart = /<!--\s*AIOS-MANAGED-START:\s*[a-zA-Z0-9_-]+\s*-->/.test(content);
const hasEnd = /<!--\s*AIOS-MANAGED-END:\s*[a-zA-Z0-9_-]+\s*-->/.test(content);
return hasStart && hasEnd;
}
/**
* Get all AIOS section IDs from content
* @param {string} content - Markdown content
* @returns {string[]} Array of section IDs
*/
function getAiosSectionIds(content) {
const ids = [];
const matches = content.matchAll(/<!--\s*AIOS-MANAGED-START:\s*([a-zA-Z0-9_-]+)\s*-->/g);
for (const match of matches) {
ids.push(match[1]);
}
return ids;
}
module.exports = {
slugify,
parseMarkdownSections,
hasAiosMarkers,
getAiosSectionIds,
};