/**
 * intent_expander — small utility / helper functions shared across modules.
 *
 * Pure helpers with no cross-file internal dependencies (except types).
 */
import path from "node:path";
import { findToolInvocationContractByName } from "../../contracts/tool_invocation_contract_registry.js";
import { resolveTaskScope } from "../scope_resolver.js";
// ── 产物类型映射 ──
export function mapToArtifactKind(kind) {
    if (kind === "unknown")
        return "custom";
    if (kind === "design_doc")
        return "design_doc";
    if (kind === "api_spec")
        return "api_spec";
    if (kind === "task_breakdown")
        return "task_breakdown";
    if (kind === "root_cause")
        return "root_cause";
    if (kind === "acceptance_checklist")
        return "acceptance_checklist";
    if (kind === "interaction_spec")
        return "interaction_spec";
    if (kind === "domain_model")
        return "domain_model";
    // 其他类型均为合法的 ArtifactKind 值
    return kind;
}
/** 将 PromptInjectionPlan 渲染为 Markdown 段落（统一注入，避免模板内重复） */
export function renderPromptInjectionPlan(plan) {
    const sections = [];
    if (plan.evidence_refs.length > 0) {
        sections.push("## 材料证据引用\n" + plan.evidence_refs.map((r) => "- " + r).join("\n"));
    }
    if (plan.required_summary.length > 0) {
        sections.push("## 必需材料摘要\n" + plan.required_summary.map((s) => "- " + s).join("\n"));
    }
    if (plan.forbidden_notes.length > 0) {
        sections.push("## 禁止/不可用材料\n" + plan.forbidden_notes.map((n) => "- " + n).join("\n"));
    }
    if (plan.manual_confirm_list.length > 0) {
        sections.push("## 人工确认项\n" + plan.manual_confirm_list.map((c) => "- " + c).join("\n"));
    }
    if (plan.allowed_sources.length > 0) {
        sections.push("## 允许的材料来源\n" + plan.allowed_sources.map((s) => "- " + s).join("\n"));
    }
    if (plan.forbidden_sources.length > 0) {
        sections.push("## 禁止的材料来源\n" + plan.forbidden_sources.map((s) => "- " + s).join("\n"));
    }
    return sections.length > 0 ? "---\n\n" + sections.join("\n\n") : "";
}
/**
 * 统一构造被阻断的 ExpandResult，消除 13 个 early return 的重复代码。
 */
export function buildBlockedResult(prompt, safeGoal, options) {
    return {
        task_id: "",
        prompt,
        scope: { allowed_paths: [], readonly_paths: [], new_files_allowed: false },
        acceptance: { automated: [], manual: [] },
        matched_patterns: [],
        matched_knowledge: [],
        is_legacy_code: false,
        degraded: true,
        ...(options.sensitivityLabels?.length ? { sensitivity_labels: options.sensitivityLabels } : {}),
        ...(options.redactionRecords?.length ? { redaction_records: options.redactionRecords } : {}),
        ...(options.privacyFindings?.length ? { privacy_findings: options.privacyFindings } : {}),
        ...(options.inputMaterials ? { input_materials: options.inputMaterials } : {}),
        ...(options.ingestionEvidences ? { ingestion_evidences: options.ingestionEvidences } : {}),
        ...(options.promptInjectionPlan ? { prompt_injection_plan: options.promptInjectionPlan } : {}),
        ...(options.outputArtifactRecord ? { output_artifact_record: options.outputArtifactRecord } : {}),
        ...(options.injectionReport ? { injection_report: options.injectionReport } : {}),
        ...(options.projectKnowledge ? { project_knowledge: options.projectKnowledge } : {}),
        ...(options.workflowTrace ? { workflow_trace: options.workflowTrace } : {}),
        contract: {
            goal: safeGoal,
            scope: [],
            constraints: options.constraints,
            verification: [],
            stop_conditions: options.stopConditions,
        },
    };
}
/**
 * 解析澄清答案，将路径注入 route_decision.input_materials，
 * 清除已解决的 missing_required_inputs。
 * 返回新的 route_decision（不修改原对象）。
 */
export function resolveClarificationAnswers(decision, _intent, answers) {
    const resolved = {
        ...decision,
        input_materials: decision.input_materials.map((m) => ({ ...m })),
        missing_required_inputs: [...decision.missing_required_inputs],
    };
    const addedPaths = [];
    for (const answer of answers) {
        // 提取路径 - absolute paths starting with /
        const absPathPattern = /(?<![^\s])\/[^\s\]]+/g;
        for (const m of answer.matchAll(absPathPattern)) {
            const p = m[0];
            if (p && p.length > 1 && !addedPaths.includes(p)) {
                addedPaths.push(p);
            }
        }
        // 提取相对路径 (word/word.ext 模式)
        const relPathPattern = /[\w][\w.-]*(?:\/[\w.-]+)+\.[\w]+/g;
        for (const m of answer.matchAll(relPathPattern)) {
            const p = m[0];
            if (p && !addedPaths.includes(p) && !addedPaths.some((ap) => ap.endsWith("/" + p))) {
                addedPaths.push(p);
            }
        }
    }
    for (const p of addedPaths) {
        if (resolved.input_materials.some((m) => m.path === p))
            continue;
        let kind = "file";
        if (/\.(zip|tar\.gz|gz|rar|7z|jar|war)$/i.test(p))
            kind = "archive";
        else if (/\.log$/i.test(p))
            kind = "log";
        else if (/\.(md|txt|doc|pdf|docx)$/i.test(p))
            kind = "doc";
        else if (/\.(java|py|ts|tsx|js|jsx|go|rs)$/i.test(p))
            kind = "file";
        resolved.input_materials.push({ kind, path: p, required: true });
    }
    // 仅清除路径类型的缺失输入（保留非路径问题如"哪个框架?"）
    if (addedPaths.length > 0) {
        resolved.missing_required_inputs = resolved.missing_required_inputs.filter((m) => !/path|路径|目录|文件|folder|directory|file/i.test(m));
    }
    return resolved;
}
/**
 * 获取 sf_expand 契约的 next_allowed_tools 和 forbidden_tools。
 */
export function getExpandContractTools() {
    const contract = findToolInvocationContractByName("sf_expand");
    return {
        next_allowed_tools: contract?.default_next_tools ?? [],
        forbidden_tools: contract?.forbidden_next_tools ?? [],
    };
}
export function buildCheckedItems(_contract, routeDecision) {
    const items = [
        "workflow_resolved",
        "mutation_allowed_checked",
        "prompt_template_checked",
        "required_mechanisms_declared",
    ];
    if (routeDecision && !routeDecision.mutation_allowed) {
        items.push("read_only_scope_checked");
    }
    return items;
}
export function mergeKnowledgeEntries(entries) {
    const seen = new Set();
    const merged = [];
    for (const entry of entries) {
        if (seen.has(entry.id))
            continue;
        seen.add(entry.id);
        merged.push(entry);
    }
    return merged;
}
export function extractKeywords(intent) {
    return intent
        .replace(/[的了吗呢把被在从对和与或是]/g, " ")
        .split(/[\s,，、；;]+/)
        .filter((w) => w.length >= 2);
}
/**
 * 读取分类结果中的受影响仓库。
 * 旧任务上下文或外部适配器可能缺少 affected_repos；这里统一降级为空数组，避免 sf_expand 卡死。
 */
export function getAffectedRepos(classification) {
    return Array.isArray(classification?.affected_repos)
        ? classification.affected_repos.filter((repo) => typeof repo === "string" && repo.length > 0)
        : [];
}
export function buildTechStackLabel(config) {
    const parts = [];
    if (config.tech_stack.backend.framework) {
        parts.push(`${config.tech_stack.backend.framework} ${config.tech_stack.backend.version}`);
    }
    if (config.tech_stack.frontend.framework) {
        parts.push(`${config.tech_stack.frontend.framework} ${config.tech_stack.frontend.version}`);
    }
    return parts.join(" + ") || "未知";
}
/**
 * 按模板文件名精确匹配知识条目。
 * 匹配 entry.name、file basename (with/without extension)。
 */
export function matchesTemplateByName(entry, templateName) {
    const requested = templateName.trim();
    const fileName = path.basename(entry.file_path);
    const fileStem = path.basename(entry.file_path, path.extname(entry.file_path));
    return [entry.name, fileName, fileStem].some((c) => c.trim() === requested);
}
export function normalizeTemplateName(name) {
    return name.trim();
}
export function matchesOutputTemplate(entry, outputTemplate) {
    const requested = normalizeTemplateName(outputTemplate);
    const fileName = path.basename(entry.file_path);
    const fileStem = path.basename(entry.file_path, path.extname(entry.file_path));
    return [
        entry.name,
        fileName,
        fileStem,
    ].some((candidate) => normalizeTemplateName(candidate) === requested);
}
/**
 * 当 affected_repos 为空时，从意图文本和技术栈配置推断 scope。
 *
 * 原则：
 * 1. 意图中含明确前端/后端关键词 → 加入对应 scope
 * 2. 意图含通用开发关键词 + 项目配了对应技术栈 → 加入对应 scope
 * 3. 都推断不出来 → 返回空数组（不限制 scope，由关键词/路由/预算控制精度）
 */
const BACKEND_KEYWORD_RE = /后端|backend|server|api|java|spring|数据库|sql|接口实现|服务端|controller|service|mapper|repository|dao|entity|dto/i;
const FRONTEND_KEYWORD_RE = /前端|frontend|client|react|vue|页面|组件|component|ui|css|html|浏览器|前端路由|route|store|hook|props/i;
const GENERIC_DEV_RE = /实现|开发|编码|修复|重构|新增|添加|完成|写|修改|变更|改|功能|feature|fix|refactor|implement/i;
export function inferScopeFromIntentAndConfig(intent, classification, config) {
    const text = `${intent} ${classification?.task_type ?? ""}`.toLowerCase();
    const scopes = ["shared"];
    // 1. 显式关键词匹配
    if (BACKEND_KEYWORD_RE.test(text)) {
        scopes.push("backend");
    }
    if (FRONTEND_KEYWORD_RE.test(text)) {
        scopes.push("frontend");
    }
    // 2. 隐式推断：通用开发意图 + 有技术栈配置
    if (!scopes.includes("backend") && !scopes.includes("frontend")) {
        const hasBackend = config.tech_stack?.backend?.framework && config.tech_stack.backend.framework !== "unknown";
        const hasFrontend = config.tech_stack?.frontend?.framework && config.tech_stack.frontend.framework !== "unknown";
        if (GENERIC_DEV_RE.test(text)) {
            if (hasBackend)
                scopes.push("backend");
            if (hasFrontend)
                scopes.push("frontend");
        }
    }
    // 3. 只有 shared 且无技术栈 → 不限制 scope
    if (scopes.length === 1 && scopes[0] === "shared"
        && (!config.tech_stack?.backend?.framework || config.tech_stack.backend.framework === "unknown")
        && (!config.tech_stack?.frontend?.framework || config.tech_stack.frontend.framework === "unknown")) {
        return [];
    }
    return [...new Set(scopes)];
}
/**
 * 解析 scope: 非代码任务使用简化 scope。
 */
export function resolveScope(config, classification, degraded, routeDecision) {
    return resolveTaskScope({
        projectPath: config._projectPath ?? process.cwd(),
        config,
        classification,
        degraded,
        routeDecision,
    });
}
/**
 * 缺少必需输入时返回澄清结果（不生成执行 prompt）。
 */
export function buildClarificationResult(_intent, routeDecision, _config, workflowContract, safeIntent, safeGoal) {
    const clarification = routeDecision.missing_required_inputs.join("\n");
    return {
        task_id: "",
        prompt: `## 澄清请求\n\n任务: ${safeIntent}\n\n缺少必需输入:\n${clarification}\n\n请补充以上信息后重新调用。`,
        scope: { allowed_paths: [], readonly_paths: [], new_files_allowed: false },
        acceptance: { automated: [], manual: [] },
        matched_patterns: [],
        matched_knowledge: [],
        is_legacy_code: false,
        degraded: true,
        contract: {
            goal: safeGoal,
            scope: [],
            constraints: ["缺少必需输入，等待澄清"],
            verification: [],
            stop_conditions: [],
        },
        workflow_trace: workflowContract ? {
            workflow_id: workflowContract.id,
            route: routeDecision.route,
            execution_shape: routeDecision.execution_shape,
            mutation_allowed: workflowContract.mutation_allowed,
            required_mechanisms: workflowContract.required_mechanisms,
            governance_findings: [],
            degraded: true,
            state_transition_target: "clarifying",
        } : undefined,
    };
}
//# sourceMappingURL=helpers.js.map