import memoize from 'lodash-es/memoize.js' import { basename } from 'path' import type { OutputStyleConfig } from '../constants/outputStyles.js' import { logForDebugging } from '../utils/debug.js' import { coerceDescriptionToString } from '../utils/frontmatterParser.js' import { logError } from '../utils/log.js' import { extractDescriptionFromMarkdown, loadMarkdownFilesForSubdir, } from '../utils/markdownConfigLoader.js' import { clearPluginOutputStyleCache } from '../utils/plugins/loadPluginOutputStyles.js' /** * Loads markdown files from .claude/output-styles directories throughout the project * and from ~/.claude/output-styles directory and converts them to output styles. * * Each filename becomes a style name, and the file content becomes the style prompt. * The frontmatter provides name and description. * * Structure: * - Project .claude/output-styles/*.md -> project styles * - User ~/.claude/output-styles/*.md -> user styles (overridden by project styles) * * @param cwd Current working directory for project directory traversal */ export const getOutputStyleDirStyles = memoize( async (cwd: string): Promise => { try { const markdownFiles = await loadMarkdownFilesForSubdir( 'output-styles', cwd, ) const styles = markdownFiles .map(({ filePath, frontmatter, content, source }) => { try { const fileName = basename(filePath) const styleName = fileName.replace(/\.md$/, '') // Get style configuration from frontmatter const name = (frontmatter['name'] || styleName) as string const description = coerceDescriptionToString( frontmatter['description'], styleName, ) ?? extractDescriptionFromMarkdown( content, `Custom ${styleName} output style`, ) // Parse keep-coding-instructions flag (supports both boolean and string values) const keepCodingInstructionsRaw = frontmatter['keep-coding-instructions'] const keepCodingInstructions = keepCodingInstructionsRaw === true || keepCodingInstructionsRaw === 'true' ? true : keepCodingInstructionsRaw === false || keepCodingInstructionsRaw === 'false' ? false : undefined // Warn if force-for-plugin is set on non-plugin output style if (frontmatter['force-for-plugin'] !== undefined) { logForDebugging( `Output style "${name}" has force-for-plugin set, but this option only applies to plugin output styles. Ignoring.`, { level: 'warn' }, ) } return { name, description, prompt: content.trim(), source, keepCodingInstructions, } } catch (error) { logError(error) return null } }) .filter(style => style !== null) return styles } catch (error) { logError(error) return [] } }, ) export function clearOutputStyleCaches(): void { getOutputStyleDirStyles.cache?.clear?.() loadMarkdownFilesForSubdir.cache?.clear?.() clearPluginOutputStyleCache() }