/**
 * Default `Reemit` implementation — builds a feedback prompt from
 * verifier errors + previous file content and calls the LLM to
 * re-emit a single file.
 *
 * The realize-emit skill (at ~/.claude/skills/realize-emit/) is
 * auto-loaded by claude-cli when its description matches the prompt
 * shape — that carries all the per-owner emission conventions
 * (typing rules, _input convention, γ-stub shape, etc.) so the
 * feedback re-emit benefits from the same guidance as the original
 * emission.
 *
 * The prompt is intentionally minimal:
 *   - Errors (verifier-formatted)
 *   - Previous file content
 *   - A request to re-emit
 *
 * Future iterations may augment with AVAILABLE SPEC SURFACE (cross-
 * service signatures) when the errors are arity-shape — empirically,
 * the skill + basic prompt suffices for most null-safety / type
 * mismatch / unused-decl cases.
 */
import { generateText } from 'ai';
import { assembleSystemWithSkills } from '../../ai/prompt-runner.js';
import { resolveProviderId } from '../../ai/model-resolver.js';
/**
 * Build a `Reemit` callback that uses the provided LLM model.
 *
 * The returned closure can be passed straight to `runPostEmitFeedback`.
 */
export function buildLlmReemit(options) {
    const maxAttempts = options.maxAttempts ?? 1;
    const timeoutMs = options.timeoutMs ?? 3 * 60_000;
    const skills = options.skills ?? ['realize-emit'];
    const contextLoader = options.contextLoader;
    // Provider-aware skill inlining: claude-cli auto-loads skills via the
    // CLI's filesystem resolution; everyone else needs the skill content
    // prepended to the system prompt. Compute once at build time — the
    // provider can't change mid-loop.
    const providerId = resolveProviderId();
    const systemPrompt = assembleSystemWithSkills('', providerId, skills);
    return async (req) => {
        const ownerContext = contextLoader?.(req.ownerName) ?? null;
        const prompt = formatFeedbackPrompt(req, ownerContext);
        for (let attempt = 0; attempt < maxAttempts; attempt++) {
            try {
                const result = await Promise.race([
                    generateText({
                        model: options.model,
                        prompt,
                        // For non-claude-cli providers, `systemPrompt` carries the
                        // skill content; for claude-cli it's empty and the CLI
                        // auto-loads.
                        ...(systemPrompt ? { system: systemPrompt } : {}),
                    }),
                    new Promise((_, reject) => setTimeout(() => reject(new Error('reemit timeout')), timeoutMs)),
                ]);
                const extracted = extractTypeScriptBlock(result.text ?? '');
                if (!extracted) {
                    if (attempt + 1 < maxAttempts)
                        continue;
                    return null;
                }
                return extracted;
            }
            catch (err) {
                if (attempt + 1 < maxAttempts)
                    continue;
                return null;
            }
        }
        return null;
    };
}
/**
 * Format the feedback prompt body. Caller's LLM provider (e.g.
 * claude-cli) loads the realize-emit skill as system context.
 *
 * The optional `ownerContext` (Phase 3) is the per-owner realize-context
 * snapshot — TARGET RUNTIME + [PRE-BAKED] step bullets + cross-service
 * surface + capabilities — prepended to the prompt so the LLM sees the
 * same input the original per-owner emit had. Without this, smaller
 * models (Ollama / MarrBox) hallucinate cross-file calls; with it,
 * they have the canonical contract to anchor against.
 *
 * Exported for tests + so callers can override the prompt shape.
 */
export function formatFeedbackPrompt(req, ownerContext = null) {
    const errorLines = req.errors
        .map(formatError)
        .join('\n');
    const parts = [];
    if (ownerContext) {
        parts.push(ownerContext);
        parts.push('');
        parts.push('---');
        parts.push('');
    }
    parts.push(`Re-emit the entire contents of \`${req.file}\`.`, '', `Your previous emission had the following compile / lint errors:`, '', errorLines, '', `Previous file content (re-emit this with the errors fixed):`, '', '```typescript', req.previousContent, '```', '', `Fix the errors and emit the ENTIRE updated file inside one \`\`\`typescript code block.`, `Honour the [PRE-BAKED] step bodies above verbatim where present; do not paraphrase.`, `Do not change any logic beyond what's needed to fix the errors.`);
    return parts.join('\n');
}
function formatError(e) {
    const pos = e.line !== undefined
        ? `:${e.line}${e.col !== undefined ? `:${e.col}` : ''}`
        : '';
    return `  ${e.file}${pos} [${e.code}] ${e.message}`;
}
/**
 * Pull the TypeScript code out of the LLM response. Matches the
 * canonical ```typescript ... ``` block; falls back to ``` ... ``` if
 * the LLM omitted the language tag.
 */
function extractTypeScriptBlock(text) {
    if (!text)
        return null;
    const tsMatch = /```typescript\s*\n([\s\S]*?)\n```/.exec(text);
    if (tsMatch)
        return tsMatch[1];
    const anyMatch = /```\s*\n([\s\S]*?)\n```/.exec(text);
    if (anyMatch)
        return anyMatch[1];
    return null;
}
//# sourceMappingURL=reemit.js.map