// engine/context-md-generator.ts — SPEC-635: Live ≤800-token context briefing for any LLM
import { writeFile, mkdir, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { specStore } from '../storage/index.js';
import { stripFrontmatter } from './frontmatter-parser.js';
const MAX_TOKENS = 800;
const CHARS_PER_TOKEN = 4; // rough estimate
const MAX_CHARS = MAX_TOKENS * CHARS_PER_TOKEN;
function truncate(text, maxChars) {
    if (text.length <= maxChars) {
        return text;
    }
    return text.slice(0, maxChars - 3) + '...';
}
async function readPendingCriteria(spec) {
    if (!spec.specPath) {
        return [];
    }
    try {
        const raw = await readFile(spec.specPath, 'utf-8');
        const body = stripFrontmatter(raw);
        const lines = body.split('\n');
        const criteria = [];
        let inSection = false;
        for (const line of lines) {
            const trimmed = line.trim();
            if (/^#{1,6}\s+(acceptance\s+criteria|criteria)\b/i.test(trimmed)) {
                inSection = true;
                continue;
            }
            if (/^#{1,6}\s+/.test(trimmed)) {
                inSection = false;
            }
            if (inSection && /^-\s+/.test(trimmed)) {
                criteria.push(trimmed.slice(2).trim());
            }
        }
        return criteria.slice(0, 5); // max 5 criteria in context.md
    }
    catch {
        return [];
    }
}
function formatSpec(spec, pendingCriteria) {
    const modelHint = ` | **Model**: ${spec.estimation.recommendedModel}`;
    const lines = [
        `## Active: ${spec.id} — ${spec.title}`,
        `**Status**: ${spec.status} | **Branch**: ${spec.gitBranch || 'not branched'}${modelHint}`,
    ];
    if (pendingCriteria.length > 0) {
        lines.push('**Pending criteria**:');
        for (const c of pendingCriteria.slice(0, 3)) {
            lines.push(`- ${c.slice(0, 120)}`);
        }
    }
    return lines.join('\n');
}
function formatBacklog(specs) {
    if (specs.length === 0) {
        return '';
    }
    const lines = ['## Backlog (top 3 by priority)'];
    for (const s of specs.slice(0, 3)) {
        lines.push(`- **${s.id}** — ${s.title} (${String(s.estimation.devHours)}h)`);
    }
    return lines.join('\n');
}
/**
 * SPEC-635: Generate planu/context.md — a ≤800-token LLM briefing of current work.
 * Updated on every spec status change. Readable by any LLM client as a plain file.
 */
export async function generateContextMd(projectPath, projectId) {
    try {
        const specs = await specStore.listSpecs(projectId);
        const implementing = specs.filter((s) => s.status === 'implementing');
        const backlog = specs
            .filter((s) => s.status === 'draft' || s.status === 'approved')
            .sort((a, b) => b.estimation.devHours - a.estimation.devHours);
        const date = new Date().toISOString().substring(0, 10);
        const lines = [`# Planu Context — ${date}`, ''];
        if (implementing.length === 0) {
            lines.push('*No spec currently implementing.*', '');
            lines.push(formatBacklog(backlog));
        }
        else {
            for (const spec of implementing.slice(0, 2)) {
                const pending = await readPendingCriteria(spec);
                lines.push(formatSpec(spec, pending), '');
            }
            if (backlog.length > 0) {
                lines.push(formatBacklog(backlog));
            }
        }
        const content = truncate(lines.join('\n'), MAX_CHARS);
        const planuDir = join(projectPath, 'planu');
        await mkdir(planuDir, { recursive: true });
        await writeFile(join(planuDir, 'context.md'), content + '\n', 'utf-8');
    }
    catch {
        /* best-effort — never blocks status transition */
    }
}
//# sourceMappingURL=context-md-generator.js.map