/** * kingdee-header.ts — 金蝶产品画像注入 + 新需求优先 kd-grill */ import type { ExtensionAPI, BeforeAgentStartEventResult } from "@kingdee-kcode/coding-agent"; import { SKILL_PROMPT_MESSAGE_TYPE, type SkillPromptDetails } from "@kingdee-kcode/coding-agent/session/messages"; import type { CustomMessageEntry, SessionEntry } from "@kingdee-kcode/coding-agent/session/session-entries"; import { getSkillProductBinding, inferProductLineIntentFromText, resolveProductLinesFromSkillNames, } from "../src/project/product-line-registry"; import { isProductProfileUnset } from "../src/project/product-line-check"; import { readProductsFromProjectMd } from "../src/project/product-md"; import { resolveProjectMdPath, TRELLIS_PROJECT_SPEC } from "../src/project/openspec-paths"; import { buildGrillSoftHint, withGrillPreferred } from "../src/project/grill-soft-hint"; function collectProductSkillNames(entries: SessionEntry[]): string[] { const names: string[] = []; for (const entry of entries) { if (entry.type !== "custom_message") continue; const cm = entry as CustomMessageEntry; if (cm.customType !== SKILL_PROMPT_MESSAGE_TYPE) continue; const name = cm.details?.name?.trim(); if (name && getSkillProductBinding(name)) names.push(name); } return names; } function suggestSkillsFromProfile(productIds: string[]): string[] { const ids = productIds.map(id => id.toLowerCase()); if (ids.some(id => id.includes("openapi"))) return ["kd-enterprise-openapi"]; if (ids.some(id => id.includes("ironpython") || id.includes("python"))) return ["kd-enterprise-python-plugin"]; if (ids.some(id => id.includes("enterprise"))) return ["kd-enterprise-csharp"]; if (ids.some(id => id.includes("cosmic"))) return ["ok-cosmic"]; return []; } function appendTurnProductIntent(lines: string[], prompt: string, preferGrill: boolean): boolean { const intent = inferProductLineIntentFromText(prompt); if (intent.productIds.length === 0) return false; const skills = withGrillPreferred(intent.skills, preferGrill); lines.push(`- 本轮用户需求明确指向:${intent.productIds.join(", ")}(${intent.reason})。`); lines.push(`- 建议优先读取 skill:${skills.join(", ")};这是本轮计划事实/高置信线索,不自动写入项目画像。`); if (preferGrill) { lines.push("- 其中 `kd-grill` 优先用于澄清;不阻塞编码,可在注释中带假设后实现。"); } lines.push("- 在 plan 阶段不得继续报“产品未知”;进入 execute 前应在计划中写明本轮线索,并提示需要 `kcode init` 写回长期画像。"); return true; } export default function (pi: ExtensionAPI) { pi.on("before_agent_start", async (event, ctx): Promise => { const projectPath = resolveProjectMdPath(ctx.cwd); const productIds = projectPath ? readProductsFromProjectMd(ctx.cwd) : []; const sessionSkills = collectProductSkillNames(ctx.sessionManager.getBranch()); const fromSkills = resolveProductLinesFromSkillNames(sessionSkills); const grill = buildGrillSoftHint(event.prompt); const lines: string[] = ["", "## 金蝶产品画像"]; if (!projectPath) { lines.push(`- 未找到 \`${TRELLIS_PROJECT_SPEC}\`。`); if (!appendTurnProductIntent(lines, event.prompt, grill.shouldPreferGrill)) { lines.push("- 当前需求也没有明确产品线;请先运行 `kcode init --cosmic` 或 `kcode init --enterprise`。"); } } else if (isProductProfileUnset(productIds)) { lines.push(`- \`${TRELLIS_PROJECT_SPEC}\` 已存在,但「产品画像」仍为未选择。`); if (!appendTurnProductIntent(lines, event.prompt, grill.shouldPreferGrill)) { lines.push("- 当前需求也没有明确产品线;请重新运行 `kcode init --cosmic` 或 `kcode init --enterprise`。"); } } else { lines.push(`- 已确认(\`${TRELLIS_PROJECT_SPEC}\`):`); for (const id of productIds) { lines.push(` - ${id}`); } if (grill.shouldPreferGrill) { const intent = inferProductLineIntentFromText(event.prompt); const skills = withGrillPreferred( intent.skills.length > 0 ? intent.skills : suggestSkillsFromProfile(productIds), true, ); lines.push(`- 本轮交付类需求:建议优先 skill:${skills.join(", ")}(含 kd-grill 澄清,不阻塞编码)。`); } } if (sessionSkills.length > 0) { lines.push(`- 本会话已加载的金蝶产品线 skill:${sessionSkills.join(", ")}`); if (fromSkills.conflict) { lines.push("- 注意:互斥产品线 skill 同时加载。以已确认产品画像为准;如画像不对,请用户重新运行对应的 `kcode init --`。"); } } lines.push(""); lines.push("产品线来源:长期真源是 `kcode init --cosmic|--enterprise` 写入的产品画像;当前用户需求中明说“星空企业版/企业版/标准版/苍穹/星瀚”等产品实体时,可作为本轮计划事实,不得报产品未知;不要用代码库关键词覆盖画像。"); lines.push("项目上下文:根 AGENTS.md、CLAUDE.md、.trellis/workflow.md、.trellis/spec/。"); lines.push("kd_cosmic_qa 不向用户询问数字 productId;企业版默认金蝶AI星空企业版/标准版,Cosmic 项目用 KCode question 让用户在金蝶AI苍穹 / 金蝶AI星瀚 / 金蝶AI套件中选择,并传入 communityProduct。第三方对接门禁只适用于外部系统/OpenAPI/HTTP/API 同步;报表、账表、字段扩展、继承原有插件取数走企业版插件 skill。"); lines.push(""); if (grill.systemLines.length > 0) { lines.push(...grill.systemLines); } const result: BeforeAgentStartEventResult = { systemPrompt: [...event.systemPrompt, lines.join("\n")], }; if (grill.shouldPreferGrill) { result.message = { customType: "kingdee-grill-soft", content: "本轮建议优先 kd-grill 澄清与形态选型(不阻塞编码)", display: true, attribution: "agent", }; } return result; }); }