import { readArtifact } from "./artifacts.ts"; import { discoverMetadataSources, formatMetadataDiscovery, type MetadataDiscoveryResult } from "./metadata-discovery.ts"; import { formatAnsweredQuestionFacts } from "./question-memory.ts"; import type { ActiveRun, KdHarnessMode } from "./types.ts"; export type DomainIntentKind = | "third-party-integration" | "ksql-datafix" | "pushdown-convert" | "operation-plugin" | "form-plugin" | "list-plugin" | "metadata-analysis" | "documentation" | "unknown"; export type DataAccessKind = "model-api" | "sql-ksql" | "external-api" | "metadata-only" | "unknown"; export interface DomainPlan { intent: DomainIntentKind; confidence: "high" | "medium" | "low"; dataAccess: DataAccessKind; suggestedMode: KdHarnessMode; signals: string[]; firstActions: string[]; requiredEvidence: string[]; playbook: string[]; metadata: MetadataDiscoveryResult; } export function buildDomainPlan(cwd: string, run: ActiveRun): DomainPlan { const text = planningText(cwd, run); const signals = collectSignals(text); const intent = inferIntent(signals); const dataAccess = inferDataAccess(signals, intent); const metadata = discoverMetadataSources(cwd, run); return { intent, confidence: confidenceForIntent(intent, signals), dataAccess, suggestedMode: suggestedModeForIntent(intent), signals, firstActions: firstActionsForIntent(intent, dataAccess, metadata), requiredEvidence: requiredEvidenceForIntent(intent, dataAccess, run), playbook: playbookForIntent(intent), metadata, }; } export function formatDomainPlan(plan: DomainPlan): string { return [ `intent:${plan.intent}`, `confidence:${plan.confidence}`, `dataAccess:${plan.dataAccess}`, `suggestedMode:${plan.suggestedMode}`, "signals:", plan.signals.length > 0 ? plan.signals.map((item) => `- ${item}`).join("\n") : "- 无明显领域信号。", "firstActions:", plan.firstActions.map((item) => `- ${item}`).join("\n"), "requiredEvidence:", plan.requiredEvidence.map((item) => `- ${item}`).join("\n"), "playbook:", plan.playbook.map((item, index) => `${index + 1}. ${item}`).join("\n"), "metadataDiscovery:", formatMetadataDiscovery(plan.metadata), ].join("\n"); } function planningText(cwd: string, run: ActiveRun): string { return [ run.goal ?? "", formatAnsweredQuestionFacts(run), readArtifact(cwd, run, "discuss") ?? "", readArtifact(cwd, run, "spec") ?? "", readArtifact(cwd, run, "plan") ?? "", ].join("\n"); } function collectSignals(text: string): string[] { const signals: string[] = []; addSignal(signals, text, /第三方|外部系统|接口|API|HTTP|REST|SOAP|Webhook|MES|WMS|同步|推送|拉取|回调/i, "third-party"); addSignal(signals, text, /\bKSQL\b|\bSQL\b|数据修复|回填|直接读表|存储过程|报表|UPDATE|DELETE|INSERT/i, "sql-ksql"); addSignal(signals, text, /下推|上推|转换|BOTP|生成.*单据|自动生成|关联生成/i, "pushdown-convert"); addSignal(signals, text, /操作插件|操作服务|提交|审核|反审核|按钮|BeforeOperation|AfterOperation|操作执行/i, "operation-plugin"); addSignal(signals, text, /表单插件|保存前|保存后|字段联动|字段值改变|校验|BeforeSave|AfterSave|propertyChanged/i, "form-plugin"); addSignal(signals, text, /列表|过滤|F7|基础资料过滤|列表插件|ListPlugin/i, "list-plugin"); addSignal(signals, text, /元数据|FormId|字段|实体|分录|FKERNELXML|fdata|entitydesign/i, "metadata"); addSignal(signals, text, /README|文档|说明|整理|总结|doc/i, "documentation"); return signals; } function addSignal(signals: string[], text: string, pattern: RegExp, signal: string): void { if (pattern.test(text) && !signals.includes(signal)) signals.push(signal); } function inferIntent(signals: string[]): DomainIntentKind { if (signals.includes("third-party")) return "third-party-integration"; if (signals.includes("sql-ksql")) return "ksql-datafix"; if (signals.includes("pushdown-convert")) return "pushdown-convert"; if (signals.includes("operation-plugin")) return "operation-plugin"; if (signals.includes("list-plugin")) return "list-plugin"; if (signals.includes("form-plugin")) return "form-plugin"; if (signals.includes("metadata")) return "metadata-analysis"; if (signals.includes("documentation")) return "documentation"; return "unknown"; } function inferDataAccess(signals: string[], intent: DomainIntentKind): DataAccessKind { if (signals.includes("sql-ksql") || intent === "ksql-datafix") return "sql-ksql"; if (signals.includes("third-party") || intent === "third-party-integration") return "external-api"; if (intent === "metadata-analysis") return "metadata-only"; if (intent === "documentation" || intent === "unknown") return "unknown"; return "model-api"; } function confidenceForIntent(intent: DomainIntentKind, signals: string[]): DomainPlan["confidence"] { if (intent === "unknown") return "low"; if (signals.length >= 2) return "high"; return "medium"; } function suggestedModeForIntent(intent: DomainIntentKind): KdHarnessMode { if (intent === "third-party-integration" || intent === "ksql-datafix") return "normal"; if (intent === "documentation" || intent === "metadata-analysis") return "quick"; if (intent === "operation-plugin" || intent === "form-plugin" || intent === "list-plugin") return "quick"; return "normal"; } function firstActionsForIntent(intent: DomainIntentKind, dataAccess: DataAccessKind, metadata: MetadataDiscoveryResult): string[] { const actions = ["读取当前项目结构和阶段文档,确认真实目标路径与产品画像。"]; if (intent !== "documentation") actions.push(metadata.recommendedAction); if (dataAccess === "external-api") actions.push("读取接口文档或用户提供的 API 说明;抽取认证、方向、字段映射、幂等、重试和错误补偿。"); if (dataAccess === "sql-ksql") actions.push("先确认 SQL/KSQL 表名、数据库字段名、dbName/dbKey 和回滚/备份策略;禁止用字段标识冒充数据库列名。"); if (dataAccess === "model-api") actions.push("获取最小实现信息:目标单据/FormId、触发事件、关键字段和业务规则;能从元数据自动查就查,查不到先记录风险进入 UAT。"); if (intent === "unknown") actions.push("不要生成代码;先通过 kd_ask_user 或项目文件把任务归类到插件、SQL/KSQL、对接、元数据或文档。"); return actions; } function requiredEvidenceForIntent(intent: DomainIntentKind, dataAccess: DataAccessKind, run: ActiveRun): string[] { if (run.mode !== "normal") { const evidence = ["编码规范检查或构建结果", "UAT/验证记录"]; if (intent !== "documentation") evidence.unshift("自动采集到的元数据 evidence(如存在)"); return evidence; } const evidence: string[] = []; if (intent !== "documentation") evidence.push(run.profile?.platform === "cosmic" ? "evidence/cosmic-metadata.json" : "evidence/data-source.md"); if (intent !== "documentation" && intent !== "metadata-analysis") evidence.push("evidence/sdk-signature.md"); if (dataAccess === "sql-ksql") evidence.push("evidence/ksql-lint.txt"); if (dataAccess === "external-api") evidence.push("接口文档/样例证据、外部验证或用户提供验证证据"); evidence.push("evidence/tdd-red.md", "evidence/tdd-green.md"); return [...new Set(evidence)]; } function playbookForIntent(intent: DomainIntentKind): string[] { if (intent === "third-party-integration") { return [ "确认对接方向、触发时机、接口文档、认证配置和字段映射。", "查金蝶目标对象元数据和当前项目已有客户端/工具类。", "制定幂等、超时重试、错误补偿、日志脱敏和验收样例。", "写 PLAN 后按红灯、实现、绿灯、verify、ship 执行。", ]; } if (intent === "ksql-datafix") { return [ "确认只读查证范围、表名、字段名、筛选条件、备份和回滚脚本。", "使用元数据/MCP/evidence 查证 SQL/KSQL 标识。", "生成脚本文本并运行 lint;修改类 SQL 交给用户执行。", "记录外部执行结果和残余风险后 ship。", ]; } if (intent === "pushdown-convert") { return [ "确认源单、目标单、BOTP/转换关系、触发事件和字段映射。", "查源/目标元数据、字段、实体和转换 API 签名。", "实现前记录红灯证据,按 PLAN 修改批准文件。", "构建/验证下推样例和异常路径。", ]; } if (intent === "operation-plugin" || intent === "form-plugin" || intent === "list-plugin") { return [ "获取最小信息:目标对象、触发事件、关键字段和业务规则。", "定位真实源码路径;元数据和 SDK 能自动查就查,不因暂缺阻塞首版。", "写轻量 PLAN 后实现批准文件。", "运行编码规范检查/构建,并交给用户 UAT;按反馈修复。", ]; } if (intent === "metadata-analysis") { return [ "发现或获取 FKERNELXML/fdata/MCP 查询结果。", "优先用 kd_metadata_collect/kcode meta 自动采集并写入 evidence;只需要预览时再用 kd_metadata_parse。", "从 evidence 抽取 FormId、字段、实体、表名和插件引用。", ]; } if (intent === "documentation") { return ["读取目标文档和项目上下文。", "按用户目标更新文档或说明。", "运行轻量检查并记录结果。"]; } return ["先归类任务类型。", "补齐产品画像、目标对象、触发点、数据访问方式和验收样例。", "归类后再进入对应 playbook。"]; }