// engine/spec-format/unified-spec-builder.ts — SPEC-709/SPEC-1010 Bug C: Build unified spec.md.
// Closes the SPEC-630 residue: every new spec is born unified, never written as a
// two-file spec.md + technical.md pair only to be merged on the next list_specs run.
/**
 * Combine a generated lean spec.md body and a generated lean technical.md body
 * into a single unified spec.md content. The technical body's YAML frontmatter
 * is stripped (the spec.md frontmatter already carries the spec id and metadata)
 * and its remaining content is appended as a `## Technical` section.
 *
 * SPEC-1010 Bug C — section preservation:
 * If the user's description already contains a `## Technical` and/or `## Files`
 * heading, we do NOT inject a duplicate. The user's content wins. We only
 * inject sections the user did not author themselves.
 */
export function buildUnifiedSpecContent(leanSpecBody, leanTechnicalBody) {
    const technicalBodyRaw = leanTechnicalBody.replace(/^---\n[\s\S]*?\n---(?:\n|$)/, '').trim();
    if (technicalBodyRaw.length === 0) {
        return leanSpecBody.endsWith('\n') ? leanSpecBody : `${leanSpecBody}\n`;
    }
    // SPEC-1010 review hardening: strip fenced code blocks before scanning for
    // `## Technical` / `## Files` headings so a user description that includes
    // markdown code samples (e.g. examples of what a spec looks like) does not
    // produce false positives that suppress the auto-injected sections.
    const scannable = stripFencedCodeBlocks(leanSpecBody);
    const userHasTechnical = /^##\s+Technical\b/m.test(scannable);
    const userHasFiles = /^##\s+Files\b/m.test(scannable);
    // Split the generated technical body into its constituent sections so we can
    // inject only the ones the user did not provide.
    const generatedFiles = extractSection(technicalBodyRaw, 'Files');
    // Generated technical body always starts with `## Files`. If the user already
    // authored both, return the spec body untouched.
    if (userHasTechnical && userHasFiles) {
        return leanSpecBody.endsWith('\n') ? leanSpecBody : `${leanSpecBody}\n`;
    }
    const sectionsToAppend = [];
    if (!userHasTechnical) {
        sectionsToAppend.push('## Technical', '');
    }
    if (!userHasFiles && generatedFiles !== null) {
        sectionsToAppend.push(generatedFiles);
    }
    if (sectionsToAppend.length === 0) {
        return leanSpecBody.endsWith('\n') ? leanSpecBody : `${leanSpecBody}\n`;
    }
    return `${leanSpecBody.trimEnd()}\n\n${sectionsToAppend.join('\n').trimEnd()}\n`;
}
function stripFencedCodeBlocks(body) {
    return body.replace(/```[\s\S]*?```/g, '').replace(/~~~[\s\S]*?~~~/g, '');
}
function escapeRegex(input) {
    return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
 * Extract a top-level (`## `) section from a body, returning the heading line
 * plus its content up to the next top-level heading (or EOF). Returns null
 * when the section heading is not present.
 */
function extractSection(body, sectionName) {
    const headingRe = new RegExp(`^##\\s+${escapeRegex(sectionName)}\\b.*$`, 'm');
    const match = headingRe.exec(body);
    if (!match) {
        return null;
    }
    const start = match.index;
    const afterHeading = start + match[0].length;
    const nextRe = /^##\s+\S/gm;
    nextRe.lastIndex = afterHeading;
    const next = nextRe.exec(body);
    const end = next ? next.index : body.length;
    return body.slice(start, end).trimEnd();
}
//# sourceMappingURL=unified-spec-builder.js.map