// tools/checkpoint-handler.ts — Handler for session_checkpoint tool (SPEC-220).
import { execFileSync } from 'node:child_process';
import { saveSessionState, getStagedFiles, getActiveSpecIds, } from '../storage/session-state-store.js';
import { hashProjectPath } from '../storage/base-store.js';
import { listSpecs } from '../storage/spec-store.js';
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
const SEPARATOR = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
function formatList(items) {
    if (items.length === 0) {
        return '(none)';
    }
    return items.map((i) => `- ${i}`).join('\n');
}
function buildHandoffBlock(savedAt, activeSpecs, decisions, nextSteps, stagedFiles) {
    const lines = [
        `Saved: ${savedAt}`,
        '',
        `Active specs: ${activeSpecs.length > 0 ? activeSpecs.join(', ') : '(none)'}`,
        '',
        'Decisions:',
        formatList(decisions),
        '',
        'Next steps:',
        formatList(nextSteps),
        '',
        `Staged files: ${stagedFiles.length > 0 ? stagedFiles.join(', ') : '(none)'}`,
    ];
    return lines.join('\n');
}
async function resolveActiveSpecs(projectPath, projectId) {
    // Try spec store first (in-progress specs)
    try {
        const resolvedId = projectId !== '' ? projectId : hashProjectPath(projectPath);
        const specs = await listSpecs(resolvedId);
        const inProgress = specs
            .filter((s) => s.status === 'implementing')
            .map((s) => `${s.id} (${s.status})`);
        if (inProgress.length > 0) {
            return inProgress;
        }
    }
    catch {
        // Fallback to filesystem scan
    }
    return getActiveSpecIds(projectPath);
}
// ---------------------------------------------------------------------------
// Pending HTML changes notification (SPEC-326)
// ---------------------------------------------------------------------------
function checkPlanuUncommittedChanges(projectPath) {
    try {
        const output = execFileSync('git', ['status', '--porcelain', 'planu/'], {
            cwd: projectPath,
            encoding: 'utf-8',
            timeout: 5_000,
            stdio: ['pipe', 'pipe', 'pipe'],
        });
        return output.trim().length > 0;
    }
    catch {
        return false;
    }
}
// ---------------------------------------------------------------------------
// Worktree teardown recommendation (SPEC-279)
// ---------------------------------------------------------------------------
function buildWorktreeTeardownRecommendation(activeSpecs) {
    const specNames = activeSpecs.map((s) => s.replace(/ \(implementing\)$/, '')).join(', ');
    return {
        type: 'worktree-teardown',
        message: `Parallel sprint detected — ${String(activeSpecs.length)} specs in progress (${specNames}). Run these steps to close the sprint safely:`,
        steps: [
            'For each worktree: bash scripts/auto-merge.sh  (full pipeline + merge to develop)',
            'bash scripts/prune-worktrees.sh  (clean up merged/orphan worktrees)',
            'pnpm test:coverage  (verify thresholds before promoting to main)',
            'git checkout main && git merge --ff-only develop && git push origin main',
        ],
    };
}
// ---------------------------------------------------------------------------
// Handler
// ---------------------------------------------------------------------------
export async function handleSessionCheckpoint(args) {
    const { projectPath, projectId, decisions = [], nextSteps = [] } = args;
    const savedAt = new Date().toISOString();
    const stagedFiles = getStagedFiles(projectPath);
    const activeSpecs = await resolveActiveSpecs(projectPath, projectId);
    await saveSessionState(projectPath, {
        savedAt,
        activeSpecs,
        recentDecisions: decisions.slice(0, 5),
        stagedFiles,
        nextSteps,
    });
    const handoff = buildHandoffBlock(savedAt, activeSpecs, decisions, nextSteps, stagedFiles);
    // SPEC-256: always include /btw tip in checkpoint recommendations
    const recommendations = [
        {
            type: 'context-tip',
            message: 'Use /btw for side questions during implementation — keeps context clean for the task. Example: /btw what does useEffect cleanup do?',
            command: '/btw your question here',
        },
    ];
    // SPEC-279: emit worktree teardown instructions when a parallel sprint is detected
    if (activeSpecs.length >= 2) {
        recommendations.push(buildWorktreeTeardownRecommendation(activeSpecs));
    }
    // SPEC-326: Pending HTML changes notification
    const planuChanges = checkPlanuUncommittedChanges(projectPath);
    const planuReminder = planuChanges
        ? '\n\n📁 Spec changes pending — commit to save: `git add planu/specs/ planu/conventions.json && git commit -m "chore(planu): save spec updates"`'
        : '';
    const text = [
        `Session checkpoint saved — ${savedAt}`,
        '',
        'Session Handoff Block (paste in new session):',
        SEPARATOR,
        handoff,
        SEPARATOR,
    ].join('\n') + planuReminder;
    return {
        content: [{ type: 'text', text }],
        structuredContent: {
            recommendations,
        },
    };
}
//# sourceMappingURL=checkpoint-handler.js.map