// engine/session-context-generator.ts — SPEC-496: Auto-generate portable session-context.md
// Writes planu/session-context.md so any LLM (Cursor, Windsurf, Aider…) can pick up project state.
import { writeFile, readFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { specStore, lessonsStore } from '../storage/index.js';
/** Reads the `version` field from the project's package.json, or returns 'unknown'. */
async function readProjectVersion(projectPath) {
    try {
        const raw = await readFile(join(projectPath, 'package.json'), 'utf-8');
        const parsed = JSON.parse(raw);
        if (parsed !== null &&
            typeof parsed === 'object' &&
            'version' in parsed &&
            typeof parsed.version === 'string') {
            const v = parsed.version;
            if (v !== undefined) {
                return v;
            }
        }
    }
    catch {
        /* best-effort */
    }
    return 'unknown';
}
/** Formats an ISO date string as YYYY-MM-DD. */
function toDateString(iso) {
    if (!iso) {
        return '—';
    }
    return iso.slice(0, 10);
}
/** Returns the last 5 specs with status 'done', ordered by updatedAt desc. */
function getRecentlyDone(specs) {
    return specs
        .filter((s) => s.status === 'done')
        .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
        .slice(0, 5);
}
/** Returns all specs with status 'approved', ordered by devHours desc. */
function getPendingApproved(specs) {
    return specs
        .filter((s) => s.status === 'approved')
        .sort((a, b) => b.estimation.devHours - a.estimation.devHours);
}
/** Fetches the last 3 lessons for the project via lessonsStore (best-effort). */
async function fetchRecentLessons(projectPath) {
    try {
        const lessons = await lessonsStore.getLessons(projectPath, { limit: 3 });
        return lessons.slice(-3).reverse();
    }
    catch {
        return [];
    }
}
/** Builds the markdown rows for the "Recently Completed" table. */
function buildDoneRows(done) {
    if (done.length === 0) {
        return '| — | No completed specs yet | — |';
    }
    return done.map((s) => `| ${s.id} | ${s.title} | ${toDateString(s.updatedAt)} |`).join('\n');
}
/** Builds the markdown rows for the "Pending Work" table. */
function buildApprovedRows(approved) {
    if (approved.length === 0) {
        return '| — | No approved specs pending | — |';
    }
    return approved
        .map((s) => `| ${s.id} | ${s.title} | ${s.estimation.devHours.toFixed(1)}h |`)
        .join('\n');
}
/** Renders the Key Lessons section. */
function buildLessonsSection(lessons) {
    if (lessons.length === 0) {
        return '- No lessons recorded yet';
    }
    return lessons.map((l) => `- ${l.title}`).join('\n');
}
/** Generates the full session-context.md content. */
function buildMarkdown(params) {
    const { version, doneCount, approvedCount, doneRows, approvedRows, lessonsContent, generatedAt } = params;
    return [
        '# Planu — Session Context',
        '',
        '> Auto-generated by Planu. Do not edit manually — it will be overwritten.',
        `> Last updated: ${generatedAt}`,
        '',
        '## Current State',
        `- **Version**: ${version}`,
        `- **Status**: ${String(doneCount)} specs done / ${String(approvedCount)} approved pending`,
        '',
        '## Recently Completed (last 5)',
        '| ID | Title | Completed |',
        '|----|-------|-----------|',
        doneRows,
        '',
        '## Pending Work (approved, by priority)',
        '| ID | Title | Est. hours |',
        '|----|-------|------------|',
        approvedRows,
        '',
        '## Key Lessons',
        lessonsContent,
        '',
        '---',
        '*Generated by [Planu](https://planu.dev) — The complete SDD stack for AI agents*',
    ].join('\n');
}
/**
 * Auto-generates `planu/session-context.md` for a project.
 * Entirely best-effort: never throws, never blocks the caller.
 */
export async function generateSessionContext(projectPath, projectId) {
    try {
        const [version, allSpecs, lessons] = await Promise.all([
            readProjectVersion(projectPath),
            specStore.listSpecs(projectId),
            fetchRecentLessons(projectPath),
        ]);
        const done = getRecentlyDone(allSpecs);
        const approved = getPendingApproved(allSpecs);
        const content = buildMarkdown({
            version,
            doneCount: allSpecs.filter((s) => s.status === 'done').length,
            approvedCount: approved.length,
            doneRows: buildDoneRows(done),
            approvedRows: buildApprovedRows(approved),
            lessonsContent: buildLessonsSection(lessons),
            generatedAt: new Date().toISOString(),
        });
        const planuDir = join(projectPath, 'planu');
        await mkdir(planuDir, { recursive: true });
        const contextPath = join(planuDir, 'session-context.md');
        await writeFile(contextPath, content, 'utf-8');
        if (process.env.PLANU_ENABLE_AUTOCOMMIT !== 'true') {
            return;
        }
        // SPEC-598: opt-in atomic commit for hosts that explicitly enable Planu autocommit.
        try {
            const { execFile } = await import('node:child_process');
            const { promisify } = await import('node:util');
            const run = promisify(execFile);
            await run('git', ['add', '--', contextPath], { cwd: projectPath });
            await run('git', ['commit', '--no-verify', '-m', 'docs(planu): update session-context', '--', contextPath], { cwd: projectPath });
        }
        catch {
            // best-effort — never blocks generation (commit fails silently if nothing changed)
        }
    }
    catch (err) {
        console.error('[session-context-generator] Failed to generate session-context.md:', err);
    }
}
//# sourceMappingURL=session-context-generator.js.map