/**
 * Skill-Specific Memory Files
 *
 * Writes learnings to both:
 * 1. CLAUDE.md Skill Memories section (existing)
 * 2. .specweave/skill-memories/{skill-name}.md (new)
 *
 * When a skill loads, it can read its specific memory file for context.
 *
 * @module core/reflection/skill-memories
 */
import * as fs from 'fs';
import * as path from 'path';
/**
 * Default directory for skill-specific memory files (relative to project root)
 */
export const SKILL_MEMORY_DIR = '.specweave/skill-memories';
/**
 * Resolve the skill memory directory for a project.
 *
 * Cascading priority (first existing dir wins):
 *   1. .specweave/skill-memories/ — SpecWeave project level
 *   2. .claude/skill-memories/    — Claude Code project level
 *
 * For writes, returns the first path whose parent dir exists
 * (.specweave/ or .claude/). Falls back to .claude/skill-memories/
 * so the feature works without SpecWeave.
 */
export function getSkillMemoryDir(projectRoot) {
    const specweaveDir = path.join(projectRoot, '.specweave');
    if (fs.existsSync(specweaveDir)) {
        return '.specweave/skill-memories';
    }
    return '.claude/skill-memories';
}
/**
 * Generate the content for a skill memory file
 */
export function generateSkillMemoryContent(skillName, learnings) {
    const title = skillName
        .split('-')
        .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
        .join(' ');
    const lines = [];
    lines.push(`# ${title} Memory`);
    lines.push('');
    lines.push('<!-- Project-specific learnings for this skill -->');
    lines.push('');
    lines.push('## Learnings');
    lines.push('');
    const today = new Date().toISOString().split('T')[0];
    for (const learning of learnings) {
        // Add date prefix if not present
        if (!learning.match(/^\*\*\d{4}-\d{2}-\d{2}\*\*/)) {
            lines.push(`- **${today}**: ${learning}`);
        }
        else {
            lines.push(`- ${learning}`);
        }
    }
    lines.push('');
    return lines.join('\n');
}
/**
 * Read existing learnings from a skill memory file
 */
export function readSkillMemoryFile(projectRoot, skillName) {
    const memDir = getSkillMemoryDir(projectRoot);
    const filePath = path.join(projectRoot, memDir, `${skillName}.md`);
    if (!fs.existsSync(filePath)) {
        return [];
    }
    const content = fs.readFileSync(filePath, 'utf-8');
    const learnings = [];
    // Parse bullet points with date prefix
    const lines = content.split('\n');
    for (const line of lines) {
        const match = line.match(/^-\s+\*\*\d{4}-\d{2}-\d{2}\*\*:\s*(.+)$/);
        if (match) {
            learnings.push(match[1].trim());
        }
    }
    return learnings;
}
/**
 * Check if a learning is a duplicate (case-insensitive)
 */
function isDuplicate(existing, newLearning) {
    const normalizedNew = newLearning.toLowerCase().trim();
    for (const e of existing) {
        const normalizedExisting = e.toLowerCase().trim();
        // Exact match
        if (normalizedExisting === normalizedNew)
            return true;
        // Substring match (one contains the other)
        if (normalizedExisting.includes(normalizedNew) ||
            normalizedNew.includes(normalizedExisting)) {
            return true;
        }
    }
    return false;
}
/**
 * Write a learning to a skill-specific memory file
 *
 * Creates the file if it doesn't exist, appends if it does.
 * Deduplicates learnings.
 */
export function writeSkillMemoryFile(projectRoot, learning) {
    const memDir = getSkillMemoryDir(projectRoot);
    const dirPath = path.join(projectRoot, memDir);
    const filePath = path.join(dirPath, `${learning.skill}.md`);
    // Ensure directory exists
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
    }
    // Read existing learnings
    const existingLearnings = readSkillMemoryFile(projectRoot, learning.skill);
    // Check for duplicates
    if (isDuplicate(existingLearnings, learning.learning)) {
        return { written: false, reason: 'Duplicate learning' };
    }
    // Add new learning
    const allLearnings = [...existingLearnings, learning.learning];
    // Generate and write content atomically (temp file + rename)
    const content = generateSkillMemoryContent(learning.skill, allLearnings);
    const tempFile = `${filePath}.tmp.${Date.now()}`;
    try {
        fs.writeFileSync(tempFile, content);
        fs.renameSync(tempFile, filePath);
    }
    catch (err) {
        try {
            fs.unlinkSync(tempFile);
        }
        catch { /* ignore cleanup failure */ }
        throw err;
    }
    return { written: true };
}
/**
 * Write multiple learnings to skill-specific memory files
 *
 * Organizes learnings by skill and writes to appropriate files.
 */
export function writeSkillMemories(projectRoot, learnings) {
    let written = 0;
    let skipped = 0;
    for (const learning of learnings) {
        const result = writeSkillMemoryFile(projectRoot, learning);
        if (result.written) {
            written++;
        }
        else {
            skipped++;
        }
    }
    return { written, skipped };
}
/**
 * Get all skill memory files in a project
 */
export function listSkillMemoryFiles(projectRoot) {
    const memDir = getSkillMemoryDir(projectRoot);
    const dirPath = path.join(projectRoot, memDir);
    if (!fs.existsSync(dirPath)) {
        return [];
    }
    return fs
        .readdirSync(dirPath)
        .filter((f) => f.endsWith('.md'))
        .map((f) => f.replace('.md', ''));
}
/**
 * Read all existing learnings from all skill memory files
 * Used for passing to LLM extraction prompt to avoid semantic duplicates
 */
export function readAllSkillMemories(projectRoot) {
    const skills = listSkillMemoryFiles(projectRoot);
    const allLearnings = [];
    for (const skill of skills) {
        const memDir = getSkillMemoryDir(projectRoot);
        const filePath = path.join(projectRoot, memDir, `${skill}.md`);
        if (!fs.existsSync(filePath))
            continue;
        const content = fs.readFileSync(filePath, 'utf-8');
        const lines = content.split('\n');
        for (const line of lines) {
            const match = line.match(/^-\s+\*\*(\d{4}-\d{2}-\d{2})\*\*:\s*(.+)$/);
            if (match) {
                allLearnings.push({
                    skill,
                    date: match[1],
                    learning: match[2].trim(),
                });
            }
        }
    }
    return allLearnings;
}
/**
 * Default pruning configuration
 */
export const DEFAULT_PRUNE_CONFIG = {
    retentionDays: 90,
    maxLearningsPerSkill: 10,
};
/**
 * Prune old learnings from skill memory files
 *
 * Removes learnings that are:
 * 1. Older than retentionDays
 * 2. Exceed maxLearningsPerSkill (keeps most recent)
 *
 * @param projectRoot - Project root path
 * @param config - Pruning configuration
 * @returns Statistics about pruning
 */
export function pruneSkillMemories(projectRoot, config = {}) {
    const fullConfig = { ...DEFAULT_PRUNE_CONFIG, ...config };
    const result = {
        prunedCount: 0,
        skillsAffected: [],
        errors: [],
    };
    const skills = listSkillMemoryFiles(projectRoot);
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - fullConfig.retentionDays);
    const cutoffDateStr = cutoffDate.toISOString().split('T')[0];
    for (const skill of skills) {
        const memDir = getSkillMemoryDir(projectRoot);
        const filePath = path.join(projectRoot, memDir, `${skill}.md`);
        try {
            const content = fs.readFileSync(filePath, 'utf-8');
            const lines = content.split('\n');
            // Parse learnings with dates
            const learnings = [];
            for (const line of lines) {
                const match = line.match(/^-\s+\*\*(\d{4}-\d{2}-\d{2})\*\*:\s*(.+)$/);
                if (match) {
                    learnings.push({
                        date: match[1],
                        learning: match[2].trim(),
                        line,
                    });
                }
            }
            const originalCount = learnings.length;
            // Filter by date (keep recent)
            let filtered = learnings.filter((l) => l.date >= cutoffDateStr);
            // Sort by date descending and limit to max
            filtered.sort((a, b) => b.date.localeCompare(a.date));
            filtered = filtered.slice(0, fullConfig.maxLearningsPerSkill);
            // Sort back by date ascending for file output
            filtered.sort((a, b) => a.date.localeCompare(b.date));
            const prunedInSkill = originalCount - filtered.length;
            if (prunedInSkill > 0) {
                // Regenerate file content atomically (temp file + rename)
                const newContent = generateSkillMemoryContent(skill, filtered.map((l) => `**${l.date}**: ${l.learning}`));
                const tempFile = `${filePath}.tmp.${Date.now()}`;
                try {
                    fs.writeFileSync(tempFile, newContent);
                    fs.renameSync(tempFile, filePath);
                }
                catch (writeErr) {
                    try {
                        fs.unlinkSync(tempFile);
                    }
                    catch { /* ignore cleanup failure */ }
                    throw writeErr;
                }
                result.prunedCount += prunedInSkill;
                result.skillsAffected.push(skill);
            }
        }
        catch (err) {
            result.errors.push(`Failed to prune ${skill}: ${err}`);
        }
    }
    return result;
}
/**
 * Format existing memories for LLM prompt
 * Groups by skill for better context
 */
export function formatMemoriesForPrompt(learnings) {
    if (learnings.length === 0) {
        return '';
    }
    // Group by skill
    const bySkill = new Map();
    for (const l of learnings) {
        const existing = bySkill.get(l.skill) || [];
        existing.push(l.learning);
        bySkill.set(l.skill, existing);
    }
    const lines = [];
    for (const [skill, skillLearnings] of bySkill) {
        lines.push(`[${skill}]`);
        for (const learning of skillLearnings) {
            lines.push(`  - ${learning}`);
        }
    }
    return lines.join('\n');
}
//# sourceMappingURL=skill-memories.js.map