/** * 产品画像检查:产品线只由 kcode init 写入;skill 只作为会话能力信号,不自动改写画像。 */ import { type ProductLineGroup, type ResolvedProductLinesFromSkills, resolveProductLinesFromSkillNames, } from "./product-line-registry"; import { readProductsFromProjectMd } from "./product-md"; export function isProductProfileUnset(productIds: string[]): boolean { return productIds.length === 0; } export interface CheckProductsFromSkillsResult { reason: string; resolved: ResolvedProductLinesFromSkills; previous: string[]; next: string[]; } /** Read-only product-line consistency check. kcode init --cosmic|--enterprise is the only supported product-line writer. */ export function checkProductsFromSkillNames(cwd: string, skillNames: string[]): CheckProductsFromSkillsResult { const resolved = resolveProductLinesFromSkillNames(skillNames); const previous = readProductsFromProjectMd(cwd); if (resolved.productIds.length === 0) { return { reason: "无金蝶产品线 skill", resolved, previous, next: previous }; } if (resolved.conflict) { return { reason: "多个互斥产品线 skill 同时加载;以 kcode init 写入的产品画像为准", resolved, previous, next: previous, }; } if (isProductProfileUnset(previous)) { return { reason: "产品画像为空;请运行 kcode init --cosmic 或 kcode init --enterprise 写入", resolved, previous, next: previous, }; } if (productsConflictWithSkillResolution(previous, resolved)) { return { reason: "已确认产品画像与当前 skill 跨族;不自动覆盖,请检查初始化是否正确", resolved, previous, next: previous, }; } return { reason: "已确认产品画像存在;skill 不改写画像", resolved, previous, next: previous }; } function productsConflictWithSkillResolution(current: string[], resolved: ResolvedProductLinesFromSkills): boolean { if (resolved.groups.length !== 1) return true; const group = resolved.groups[0]!; const cosmicIds = new Set(["cosmic-java", "cangqiong", "xinghan", "qijian", "cosmic"]); const enterpriseIds = new Set(["enterprise-csharp", "enterprise-ironpython", "enterprise", "k3cloud"]); const openapiIds = new Set(["enterprise-openapi"]); function familyOf(id: string): ProductLineGroup | "unknown" { if (cosmicIds.has(id) || id.includes("cosmic") || id.includes("cangqiong")) return "cosmic"; if (openapiIds.has(id)) return "enterprise-openapi"; if (enterpriseIds.has(id) || id.includes("enterprise")) return "enterprise"; return "unknown"; } for (const id of current) { const f = familyOf(id); if (f === "unknown") continue; if (f !== group) return true; } return false; }