// tools/context-manager-handler.ts — SPEC-370: Context Window Management
// SPEC-622: Integrated context orchestrator for automatic relief at 80%+ usage
import { estimateTokenBreakdown, suggestArchivableSpecs, archiveSpecToPath, restoreArchivedSpecFromPath, listArchivedSpecIds, } from '../engine/context-manager.js';
import { detectContextPressure, runReliefPipeline } from '../engine/context-orchestrator/index.js';
import { loadContextConfig, saveContextConfig } from '../storage/context-config-store.js';
import { specStore } from '../storage/index.js';
import { getDecisions } from '../storage/decision-store.js';
import { getLessons } from '../storage/lessons-store.js';
import { hashProjectPath, projectDataDir } from '../storage/base-store.js';
const ARCHIVE_SUBDIR = 'archived-context';
function archiveDir(projectId) {
    return `${projectDataDir(projectId)}/${ARCHIVE_SUBDIR}`;
}
function archiveFilePath(projectId, specId) {
    return `${archiveDir(projectId)}/${specId}.json`;
}
export async function handleContextWindowStatus(input) {
    const projectId = hashProjectPath(input.projectPath);
    const [specs, decisions, lessons, config] = await Promise.all([
        specStore.listSpecs(projectId),
        getDecisions(projectId).catch(() => []),
        getLessons(input.projectPath, {}).catch(() => []),
        loadContextConfig(input.projectPath),
    ]);
    const breakdown = estimateTokenBreakdown(specs.length, decisions.length, lessons.length);
    const pct = detectContextPressure(breakdown.total, config.maxTokens);
    const warn = breakdown.total >= config.warnAt;
    const lines = [
        `Context window estimate for project:`,
        ``,
        `  Specs:     ${breakdown.specs.toLocaleString('en-US')} tokens (${breakdown.specCount} specs × ~1,500)`,
        `  Decisions: ${breakdown.decisions.toLocaleString('en-US')} tokens (${breakdown.decisionCount} decisions × ~200)`,
        `  Lessons:   ${breakdown.lessons.toLocaleString('en-US')} tokens (${breakdown.lessonCount} lessons × ~150)`,
        `  ─────────────────────────────────────`,
        `  Total:     ${breakdown.total.toLocaleString('en-US')} / ${config.maxTokens.toLocaleString('en-US')} tokens (${pct}%)`,
        ``,
        warn
            ? `WARNING: Usage is above warn threshold (${config.warnAt.toLocaleString('en-US')}). Consider running optimize_context.`
            : `Status: OK — within budget.`,
        `Strategy: ${config.strategy}, archive after ${config.archiveAfterDays} days`,
    ];
    // SPEC-622: Auto-relief pipeline at 80%+ usage
    if (pct >= 80) {
        const orchestratorConfig = { disabled: false };
        const relief = await runReliefPipeline(pct, input.projectPath, orchestratorConfig);
        if (relief.actionsExecuted.length > 0 || relief.warning) {
            lines.push('', '---', relief.warning);
            if (relief.actionsExecuted.length > 0) {
                lines.push('', `Auto-relief executed: freed ~${String(relief.freedTokens)} tokens`, `Actions: ${relief.actionsExecuted.join(' | ')}`, `Context now at ${String(relief.usagePercent)}% (${String(relief.availableTokens)} tokens available)`);
            }
        }
        // SPEC-634: Write session.json with resume prompt at 80%+ context usage
        void (async () => {
            try {
                const pid = hashProjectPath(input.projectPath);
                const allSpecs = await specStore.listSpecs(pid);
                const implementing = allSpecs.filter((s) => s.status === 'implementing');
                if (implementing.length > 0) {
                    const { writeSpecSessionState } = await import('../engine/session-state/writer.js');
                    for (const spec of implementing.slice(0, 2)) {
                        await writeSpecSessionState(input.projectPath, spec.id, spec.title, 'context at 80%+ — save state', 'unknown', pct).catch(() => {
                            /* best-effort */
                        });
                    }
                    lines.push('', `⚠️ Context at ${String(pct)}% — session.json saved. Run /compact then paste the resumePrompt to continue.`);
                }
            }
            catch {
                /* best-effort */
            }
        })();
    }
    return {
        content: [{ type: 'text', text: lines.join('\n') }],
    };
}
export async function handleOptimizeContext(input) {
    const projectId = hashProjectPath(input.projectPath);
    const [config, specs] = await Promise.all([
        loadContextConfig(input.projectPath),
        specStore.listSpecs(projectId),
    ]);
    const suggestion = suggestArchivableSpecs(specs, config.archiveAfterDays);
    if (input.action === 'apply' && suggestion.specs.length > 0) {
        const archived = [];
        for (const s of suggestion.specs) {
            const spec = specs.find((sp) => sp.id === s.specId);
            if (!spec) {
                continue;
            }
            const archPath = archiveFilePath(projectId, s.specId);
            await archiveSpecToPath(archPath, spec);
            archived.push(s.specId);
        }
        return {
            content: [
                {
                    type: 'text',
                    text: [
                        `Archived ${archived.length} spec(s): ${archived.join(', ')}`,
                        `Saved ~${suggestion.potentialSavings.toLocaleString('en-US')} tokens.`,
                    ].join('\n'),
                },
            ],
        };
    }
    const specLines = suggestion.specs.map((s) => `  • ${s.specId} — ${s.title} (done ${s.daysSinceDone}d ago, ~${s.estimatedTokens} tokens)`);
    const lines = [
        suggestion.recommendation,
        ...(specLines.length > 0 ? [``, `Candidates:`, ...specLines] : []),
        ``,
        `Run with action="apply" to archive these specs.`,
    ];
    return {
        content: [{ type: 'text', text: lines.join('\n') }],
    };
}
export async function handleContextBudgetConfig(input) {
    const current = await loadContextConfig(input.projectPath);
    const updated = {
        maxTokens: input.maxTokens ?? current.maxTokens,
        strategy: input.strategy ?? current.strategy,
        warnAt: input.warnAt ?? current.warnAt,
        archiveAfterDays: input.archiveAfterDays ?? current.archiveAfterDays,
    };
    await saveContextConfig(input.projectPath, updated);
    return {
        content: [
            {
                type: 'text',
                text: [
                    `Context budget configured:`,
                    `  maxTokens: ${updated.maxTokens.toLocaleString('en-US')}`,
                    `  strategy: ${updated.strategy}`,
                    `  warnAt: ${updated.warnAt.toLocaleString('en-US')}`,
                    `  archiveAfterDays: ${updated.archiveAfterDays}`,
                ].join('\n'),
            },
        ],
    };
}
export async function handleArchiveSpecContext(input) {
    const projectId = hashProjectPath(input.projectPath);
    const spec = await specStore.getSpec(projectId, input.specId);
    if (!spec) {
        return {
            content: [{ type: 'text', text: `Spec "${input.specId}" not found.` }],
            isError: true,
        };
    }
    const archPath = archiveFilePath(projectId, input.specId);
    await archiveSpecToPath(archPath, spec);
    return {
        content: [
            {
                type: 'text',
                text: `Spec "${input.specId}" archived to archived-context/${input.specId}.json`,
            },
        ],
    };
}
export function handleCompressSpecHistory(input) {
    const keepVersions = input.keepVersions ?? 3;
    // The version history feature (SPEC-spec-version-history) stores versions separately.
    // We describe what would happen without modifying active data.
    return {
        content: [
            {
                type: 'text',
                text: [
                    `Compress history for spec "${input.specId}":`,
                    `  Keep last ${keepVersions} version(s)`,
                    `  Older versions would be removed from spec version history`,
                    ``,
                    `NOTE: Version history compression requires spec_version_history data.`,
                    `If no history exists for this spec, this is a no-op.`,
                ].join('\n'),
            },
        ],
    };
}
export async function handleRestoreArchivedContext(input) {
    const projectId = hashProjectPath(input.projectPath);
    const archived = await listArchivedSpecIds(archiveDir(projectId));
    if (!archived.includes(input.specId)) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Spec "${input.specId}" is not in archived-context/. Available: ${archived.join(', ') || 'none'}`,
                },
            ],
            isError: true,
        };
    }
    const archPath = archiveFilePath(projectId, input.specId);
    const ok = await restoreArchivedSpecFromPath(archPath);
    if (!ok) {
        return {
            content: [{ type: 'text', text: `Failed to restore "${input.specId}".` }],
            isError: true,
        };
    }
    return {
        content: [
            {
                type: 'text',
                text: `Spec "${input.specId}" restored from archived-context/.`,
            },
        ],
    };
}
//# sourceMappingURL=context-manager-handler.js.map