/**
 * Auto-learn from errors and git history.
 * Agents learn from failures without explicit hippo remember calls.
 */
import { execSync, execFileSync, spawn } from 'child_process';
import { createMemory, Layer } from './memory.js';
import { loadAllEntries } from './store.js';
import { textOverlap } from './search.js';
/**
 * Create a MemoryEntry capturing a command failure.
 * Content format: "Command '<cmd>' failed: <truncated stderr>"
 */
export function captureError(exitCode, stderr, command) {
    // Truncate to first 500 chars to avoid storing megabytes of build logs
    const wasTruncated = stderr.length > 500;
    const truncated = stderr.slice(0, 500).trim();
    const suffix = wasTruncated ? ' [truncated]' : '';
    // Strip leading env var assignments (KEY=val or key=val) before the actual command name
    const safeCmd = command.replace(/^([A-Za-z_][A-Za-z0-9_]*=\S+\s+)+/, '').trim() || '(redacted)';
    const content = `Command '${safeCmd}' failed (exit ${exitCode}): ${truncated}${suffix}`;
    // Derive a sanitized tag from the command name (first word, strip path)
    const cmdBase = safeCmd.split(/\s+/)[0].replace(/[^a-zA-Z0-9-]/g, '');
    const tags = ['error', 'autolearn'];
    if (cmdBase)
        tags.push(cmdBase.toLowerCase().slice(0, 30));
    return createMemory(content, {
        layer: Layer.Episodic,
        tags,
        source: 'autolearn',
        confidence: 'observed',
    });
}
/**
 * Parse git log output for actionable lessons.
 * Looks for fix:, revert:, bug:, error:, hotfix: commit messages.
 */
export function extractLessons(gitLog, customPatterns) {
    const lessons = [];
    const lines = gitLog.split('\n');
    // Patterns that indicate a lesson to learn from
    // Covers: fix, revert, bug, error, hotfix, refactor, perf, chore, breaking, deprecate
    const prefixes = customPatterns?.join('|') ?? 'fix|revert|bug|error|hotfix|bugfix|refactor|perf|chore|breaking|deprecate';
    const patterns = [
        new RegExp(`^[a-f0-9]+\\s+(${prefixes})(\\(.+?\\))?:?\\s+(.+)`, 'i'),
        new RegExp(`^(${prefixes})(\\(.+?\\))?:?\\s+(.+)`, 'i'),
        /^(Fix|Revert|Bug|Hotfix|Bugfix|Refactor|Perf)\s+(.+)/,
        /\b(fixed|reverted|corrected|resolved|refactored|optimized|deprecated)\b.{3,100}/i,
    ];
    for (const line of lines) {
        const trimmed = line.trim();
        if (!trimmed || trimmed.startsWith('commit ') || trimmed.startsWith('Author:') || trimmed.startsWith('Date:')) {
            continue;
        }
        // Strip leading git hash if present (real hashes are hex, but be lenient with alphanumeric prefixes)
        const subject = trimmed.replace(/^[a-z0-9]{6,40}\s+/i, '');
        for (const pat of patterns) {
            const m = subject.match(pat);
            if (m) {
                // For conventional commits: use group 3 (message after prefix), group 2, or full match
                const lesson = (m[3] ?? m[2] ?? m[0]).trim();
                if (lesson.length > 5 && lesson.length < 500) {
                    lessons.push(lesson);
                }
                break;
            }
        }
    }
    // Deduplicate exact matches at extraction time
    return [...new Set(lessons)];
}
/**
 * Check if a substantially similar memory already exists.
 * Returns true if overlap > threshold (default 0.7).
 *
 * L9: `tenantId` is opt-in. Only takes effect when the first argument is a
 * root string (string-overload path). When the first argument is a
 * pre-loaded MemoryEntry[], the caller has already scoped — tenantId is
 * ignored on that path.
 */
export function deduplicateLesson(hippoRootOrEntries, lesson, threshold = 0.7, tenantId) {
    const entries = typeof hippoRootOrEntries === 'string'
        ? loadAllEntries(hippoRootOrEntries, tenantId)
        : hippoRootOrEntries;
    for (const entry of entries) {
        const overlap = textOverlap(lesson, entry.content);
        if (overlap > threshold)
            return true;
    }
    return false;
}
/**
 * Run a command, streaming stdout/stderr to the terminal in real time.
 * Returns: { exitCode, stderr }.
 */
export function runWatched(command) {
    return new Promise((resolve) => {
        // Use shell: true so the command string is handled by the shell as-is
        const child = spawn(command, { shell: true, stdio: ['inherit', 'inherit', 'pipe'] });
        const stderrChunks = [];
        child.stderr.on('data', (chunk) => {
            stderrChunks.push(chunk);
            // Also pass through to terminal
            process.stderr.write(chunk);
        });
        child.on('close', (code) => {
            resolve({
                exitCode: code ?? 1,
                stderr: Buffer.concat(stderrChunks).toString('utf8'),
            });
        });
        child.on('error', (err) => {
            resolve({ exitCode: 1, stderr: err.message });
        });
    });
}
/**
 * Check whether a directory is a git work tree.
 */
export function isGitRepo(cwd) {
    try {
        const raw = execSync('git rev-parse --is-inside-work-tree', {
            encoding: 'utf8',
            cwd,
            timeout: 10000,
            stdio: ['ignore', 'pipe', 'ignore'],
        });
        return raw.trim() === 'true';
    }
    catch {
        return false;
    }
}
/**
 * Fetch recent git log lines (subject lines only).
 * days: how many days of history to include.
 */
export function fetchGitLog(cwd, days) {
    try {
        const raw = execFileSync('git', [
            'log', `--since=${days} days ago`, '--pretty=format:%s',
        ], { encoding: 'utf8', cwd, timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] });
        return typeof raw === 'string' ? raw : '';
    }
    catch {
        return '';
    }
}
//# sourceMappingURL=autolearn.js.map