cmte
Version:
Design by Committee™ except it's just you and LLMs
93 lines (84 loc) • 2.86 kB
JavaScript
import yaml from 'js-yaml';
import { readFile } from '../../utils/fs.js';
import { logger } from '../../utils/logger.js';
/**
* Extracts and validates frontmatter from content
* @param {string} content Content to parse
* @returns {Object} Object containing metadata and content
*/
export function extractFrontmatter(content) {
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
const match = content.match(frontmatterRegex);
if (!match) {
return {
metadata: {},
content
};
}
try {
const [_, frontmatter, remainingContent] = match;
const metadata = yaml.load(frontmatter);
return {
metadata,
content: remainingContent
};
} catch (error) {
logger.error('Error parsing frontmatter YAML', { error });
throw new Error(`Failed to parse frontmatter: ${error.message}`);
}
}
/**
* Validates thinking configuration in frontmatter
* @param {Object} metadata Frontmatter metadata
* @throws {Error} If validation fails
*/
export function validateThinkingConfig(metadata) {
if (!metadata.thinking) {
return; // No thinking config to validate
}
// Validate thinking_paragraphs if present
if (metadata.thinking_paragraphs !== undefined) {
if (!Number.isInteger(metadata.thinking_paragraphs) || metadata.thinking_paragraphs < 1) {
throw new Error('thinking_paragraphs must be a positive integer');
}
}
// Validate thinking_position if present
if (metadata.thinking_position && !['prepend', 'append'].includes(metadata.thinking_position)) {
throw new Error('thinking_position must be either "prepend" or "append"');
}
// Validate thinking_prompt if present
if (metadata.thinking_prompt && typeof metadata.thinking_prompt !== 'string') {
throw new Error('thinking_prompt must be a string');
}
}
/**
* Loads and validates a task file
* @param {string} filePath Path to task file
* @returns {Object} Task configuration and content
*/
export async function loadTaskFromFile(filePath) {
try {
const fileContent = await readFile(filePath);
const { metadata, content } = extractFrontmatter(fileContent);
// Validate thinking configuration if present
validateThinkingConfig(metadata);
// Return simplified task structure
return {
task: {
name: metadata.name,
thinking: metadata.thinking || false,
thinkingConfig: metadata.thinking ? {
paragraphs: metadata.thinking_paragraphs || 5,
position: metadata.thinking_position || 'append',
instruction: metadata.thinking_instruction,
introduction: metadata.thinking_introduction,
promptFile: metadata.thinking_prompt
} : null
},
content
};
} catch (error) {
logger.error(`Error loading task from file: ${filePath}`, { error });
throw error;
}
}