kcode-pi 0.1.34 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +10 -10
  2. package/dist/cli/kcode.js +3 -3
  3. package/dist/context/project-context.js +4 -5
  4. package/dist/harness/prompt-policy.d.ts +19 -0
  5. package/dist/harness/prompt-policy.js +206 -0
  6. package/dist/harness/types.d.ts +74 -0
  7. package/dist/harness/types.js +16 -0
  8. package/dist/product/profile.d.ts +20 -0
  9. package/dist/product/profile.js +103 -0
  10. package/docs/CHANGELOG.md +90 -2
  11. package/docs/COMMAND_REFERENCE.md +27 -12
  12. package/docs/DEVELOPMENT.md +3 -3
  13. package/docs/EVIDENCE_AND_GATES.md +15 -8
  14. package/docs/HARNESS_WORKFLOW.md +32 -12
  15. package/docs/KCODE_DISTRIBUTION.md +7 -7
  16. package/docs/PRODUCT_PROFILE.md +9 -9
  17. package/docs/TROUBLESHOOTING.md +8 -8
  18. package/docs/USER_GUIDE.md +10 -10
  19. package/extensions/kingdee-harness.ts +141 -86
  20. package/extensions/kingdee-header.ts +1 -1
  21. package/extensions/kingdee-subagents.ts +1 -1
  22. package/extensions/kingdee-tools.ts +44 -44
  23. package/package.json +1 -1
  24. package/src/cli/kcode.ts +3 -3
  25. package/src/context/project-context.ts +4 -5
  26. package/src/harness/artifacts.ts +6 -7
  27. package/src/harness/data-source-policy.ts +346 -0
  28. package/src/harness/delegation.ts +28 -23
  29. package/src/harness/gates.ts +16 -1
  30. package/src/harness/messages.ts +65 -11
  31. package/src/harness/path-policy.ts +1 -0
  32. package/src/harness/plan-steps.ts +3 -3
  33. package/src/harness/prompt-policy.ts +227 -0
  34. package/src/harness/prompt.ts +12 -16
  35. package/src/harness/question-memory.ts +220 -0
  36. package/src/harness/repair.ts +18 -3
  37. package/src/harness/state.ts +93 -6
  38. package/src/harness/types.ts +19 -0
  39. package/src/official/kingdee-skills.ts +4 -4
  40. package/src/product/profile.ts +2 -2
  41. package/src/rules/checker.ts +27 -27
  42. package/src/tools/build-debug.ts +5 -5
  43. package/src/tools/sdk-signature.ts +4 -4
@@ -1,11 +1,18 @@
1
1
  import type { KdPhase } from "./types.ts";
2
2
  import { PHASE_ARTIFACTS } from "./types.ts";
3
+ import {
4
+ DATA_SOURCE_CONTEXT_FIELDS,
5
+ IMPLEMENTATION_CONTRACT_FIELDS,
6
+ INTEGRATION_CONTEXT_FIELDS,
7
+ fieldLabels,
8
+ questionForMissingLabel,
9
+ } from "./prompt-policy.ts";
3
10
 
4
11
  export function unknownProductReason(declaration: boolean | undefined): string {
5
12
  if (declaration === undefined) {
6
- return "不能进入 execute:产品画像未知,且 PLAN.md 未明确声明是否涉及产品实现、构建、元数据或 SDK 查证。下一步:先由当前需求和项目计划判断该范围;如果涉及产品实现,执行 /kd-product <flagship|xinghan|cangqiong|enterprise>;如果不涉及,在 PLAN.md 的“产品实现范围”写明“不涉及”及依据。";
13
+ return "不能进入 execute:产品画像未知,且 PLAN.md 未明确声明是否涉及产品实现、构建、元数据或 SDK 查证。下一步:根据当前需求和项目计划判断范围;涉及产品实现时执行 /kd-product <flagship|xinghan|cangqiong|enterprise>;不涉及时在 PLAN.md 的“产品实现范围”写明“不涉及”及依据。";
7
14
  }
8
- return "不能进入 execute:PLAN.md 声明涉及产品实现、构建、元数据或 SDK 查证,但产品画像未知。下一步:根据需求、计划或用户回答确认产品,然后执行 /kd-product <flagship|xinghan|cangqiong|enterprise>;如果无法判断,先用 kd_question 只问一个最阻塞的产品确认问题。";
15
+ return "不能进入 execute:PLAN.md 声明涉及产品实现、构建、元数据或 SDK 查证,但产品画像未知。下一步:根据需求、计划或用户回答确认产品,然后执行 /kd-product <flagship|xinghan|cangqiong|enterprise>;无法判断时使用 kd_question 登记一个最阻塞的产品确认问题。";
9
16
  }
10
17
 
11
18
  export function unknownRiskReason(): string {
@@ -15,7 +22,7 @@ export function unknownRiskReason(): string {
15
22
  export function openQuestionsReason(questions: Array<{ id: string; question: string }>): string {
16
23
  return [
17
24
  `存在未回答的阻断问题:${questions.map((question) => `${question.id} ${question.question}`).join(";")}`,
18
- "下一步:先获取用户答案,然后用 kd_question action=answer id=<问题编号> answer=<用户答案> 记录。",
25
+ "下一步:获取用户答案,然后用 kd_question action=answer id=<问题编号> answer=<用户答案> 记录。",
19
26
  ].join("。");
20
27
  }
21
28
 
@@ -24,6 +31,33 @@ export function repairBlockedReason(attempts: number, maxAttempts: number, evide
24
31
  return `自动修复已停止:验证失败 ${attempts}/${maxAttempts}${evidenceText}。下一步:回到 plan 调整范围,或重新创建/回答修复问题选择“继续修复”。`;
25
32
  }
26
33
 
34
+ export function dataSourcePlanBlockedReason(missing: string[]): string {
35
+ return [
36
+ `不能进入 execute:业务数据源上下文不完整,缺少 ${missing.join("、")}。`,
37
+ `下一步:停止编码,使用 kd_question 逐项确认 ${fieldLabels(DATA_SOURCE_CONTEXT_FIELDS).join("、")}。`,
38
+ nextQuestionInstruction(missing),
39
+ "API 文档只能作为调用线索,不能替代这些业务事实。",
40
+ ].filter(Boolean).join("");
41
+ }
42
+
43
+ export function integrationPlanBlockedReason(missing: string[]): string {
44
+ return [
45
+ `不能进入 execute:第三方对接上下文不完整,缺少 ${missing.join("、")}。`,
46
+ `下一步:停止编码,使用 kd_question 逐项确认 ${fieldLabels(INTEGRATION_CONTEXT_FIELDS).join("、")}。`,
47
+ nextQuestionInstruction(missing),
48
+ "缺少这些信息时禁止生成模板代码。",
49
+ ].filter(Boolean).join("");
50
+ }
51
+
52
+ export function implementationPlanBlockedReason(missing: string[]): string {
53
+ return [
54
+ `不能进入 execute:实现就绪信息不完整,缺少 ${missing.join("、")}。`,
55
+ `下一步:禁止生成模板代码;使用 kd_question 按最阻塞项逐个确认 ${fieldLabels(IMPLEMENTATION_CONTRACT_FIELDS).join("、")}。`,
56
+ nextQuestionInstruction(missing),
57
+ "这些字段是所有插件、服务和集成实现的通用输入,不依赖具体业务场景枚举。",
58
+ ].filter(Boolean).join("");
59
+ }
60
+
27
61
  export function missingMarkerReason(phase: KdPhase, missing: string[]): string {
28
62
  return `${PHASE_ARTIFACTS[phase]} 缺少必需章节:${missing.join(", ")}。下一步:更新 ${PHASE_ARTIFACTS[phase]},补齐这些章节并写入真实内容。`;
29
63
  }
@@ -40,7 +74,7 @@ export function missingForTargetReason(target: KdPhase, artifacts: string[]): st
40
74
  const documents = artifacts.filter((artifact) => !artifact.startsWith("evidence/"));
41
75
  const actions: string[] = [];
42
76
  if (documents.length > 0) {
43
- actions.push(`先补齐阶段文档 ${documents.join(", ")},可用 /kd-artifact 创建模板后填入真实分析、计划或验证内容`);
77
+ actions.push(`补齐阶段文档 ${documents.join(", ")};可使用 /kd-artifact 创建模板后填入真实分析、计划或验证内容`);
44
78
  }
45
79
  if (evidence.length > 0) {
46
80
  actions.push(evidenceAction(evidence));
@@ -61,9 +95,11 @@ export function evidenceArtifactAction(artifact: string): string {
61
95
  case "evidence/sdk-signature.md":
62
96
  return "运行 kd_sdk_signature,从当前项目真实 SDK jar/dll 查证类、方法、构造器或属性签名,成功后自动写入 evidence/sdk-signature.md";
63
97
  case "evidence/cosmic-config.txt":
64
- return "运行 kd_cosmic_config 生成 evidence/cosmic-config.txt;如果项目没有 ok-cosmic.json,先使用 KCode 默认配置";
98
+ return "运行 kd_cosmic_config 生成 evidence/cosmic-config.txt;项目没有 ok-cosmic.json 时使用 KCode 默认配置";
65
99
  case "evidence/cosmic-metadata.json":
66
100
  return "运行 kd_cosmic_metadata 查询目标表单/单据/字段元数据并生成 evidence/cosmic-metadata.json";
101
+ case "evidence/data-source.md":
102
+ return "写入真实数据源证据 evidence/data-source.md,至少包含目标 FormId/单据或表单、字段/实体标识、表或数据来源、验证来源和测试数据;禁止只引用 API 文档";
67
103
  case "evidence/cosmic-api.txt":
68
104
  return "运行 kd_cosmic_api 查询相关 Cosmic API 线索并生成 evidence/cosmic-api.txt,再用 kd_sdk_signature 或构建输出确认签名";
69
105
  case "evidence/ksql-lint.txt":
@@ -74,7 +110,7 @@ export function evidenceArtifactAction(artifact: string): string {
74
110
  }
75
111
 
76
112
  export function flagshipWriteBlockedReason(path: string): string {
77
- return `星空旗舰版代码必须跟随当前项目结构写入 code/ 下,不能写到 ${path}。下一步:先读取当前项目 code/ 下的真实模块结构,在 PLAN.md 记录目标源码路径,再把写入路径改为 code/... 下的项目相对路径。`;
113
+ return `星空旗舰版代码必须跟随当前项目结构写入 code/ 下,不能写到 ${path}。下一步:读取当前项目 code/ 下的真实模块结构,在 PLAN.md 记录目标源码路径,再把写入路径改为 code/... 下的项目相对路径。`;
78
114
  }
79
115
 
80
116
  export function planWriteBlockedReason(path: string): string {
@@ -82,11 +118,11 @@ export function planWriteBlockedReason(path: string): string {
82
118
  }
83
119
 
84
120
  export function flagshipPlanNeedsCodePathReason(): string {
85
- return "不能进入 execute:星空旗舰版 PLAN.md 需要先记录当前项目 code/ 下的实际目标路径。下一步:列出 code/ 下模块,识别当前项目是按云、按应用还是不分模块组织,在 PLAN.md 写明真实目标文件。";
121
+ return "不能进入 execute:星空旗舰版 PLAN.md 必须记录当前项目 code/ 下的实际目标路径。下一步:列出 code/ 下模块,识别当前项目是按云、按应用还是不分模块组织,在 PLAN.md 写明真实目标文件。";
86
122
  }
87
123
 
88
124
  export function flagshipPlanNeedsSourcePathReason(): string {
89
- return "不能进入 execute:PLAN.md 需要先记录已检查当前项目结构,并写明实际源码根或目标文件路径。下一步:读取构建文件和 src/lib/bin 等目录,确认源码根后写入 PLAN.md 的 ## 已检查的项目结构、## 目标源码根 / 路径 和 ## 允许修改的文件。";
125
+ return "不能进入 execute:PLAN.md 必须记录已检查当前项目结构,并写明实际源码根或目标文件路径。下一步:读取构建文件和 src/lib/bin 等目录,确认源码根后写入 PLAN.md 的 ## 已检查的项目结构、## 目标源码根 / 路径 和 ## 允许修改的文件。";
90
126
  }
91
127
 
92
128
  export function tddPlanMissingReason(): string {
@@ -94,7 +130,7 @@ export function tddPlanMissingReason(): string {
94
130
  }
95
131
 
96
132
  export function tddProductionMissingRedReason(path: string, evidenceName: string): string {
97
- return `不能写生产源码 ${path}:缺少红灯证据 ${evidenceName}。下一步:先运行一个实现前应失败的检查,例如 kd_sdk_signature 方法不存在检查、元数据/API 检查、编译检查、kd_check、项目已有测试或外部接口最小验证;把命令、非 0 Exit 或失败输出写入 evidence/tdd-red.md 后再写生产源码。`;
133
+ return `不能写生产源码 ${path}:缺少红灯证据 ${evidenceName}。下一步:运行一个实现前应失败的检查,例如 kd_sdk_signature 方法不存在检查、元数据/API 检查、编译检查、kd_check、项目已有测试或外部接口最小验证;把命令、非 0 Exit 或失败输出写入 evidence/tdd-red.md 后再写生产源码。`;
98
134
  }
99
135
 
100
136
  export function tddVerifyBlockedReason(reasons: string[]): string {
@@ -106,13 +142,31 @@ export function tddVerifyBlockedReason(reasons: string[]): string {
106
142
  }
107
143
 
108
144
  export function redEvidenceInvalidReason(evidenceName: string): string {
109
- return `${evidenceName} 内容无效:需要包含真实失败输出或非 0 退出码`;
145
+ return `${evidenceName} 内容无效:必须包含真实失败输出或非 0 退出码`;
110
146
  }
111
147
 
112
148
  export function greenEvidenceInvalidReason(evidenceName: string): string {
113
- return `${evidenceName} 内容无效:绿灯证据需要同时包含成功结论和 Exit: 0/退出码:0`;
149
+ return `${evidenceName} 内容无效:绿灯证据必须同时包含成功结论和 Exit: 0/退出码:0`;
114
150
  }
115
151
 
116
152
  export function sdkSignatureWriteBlockedReason(path: string, evidenceName: string): string {
117
153
  return `不能写生产源码 ${path}:缺少本地 SDK 签名证据 ${evidenceName}。下一步:运行 kd_sdk_signature,从当前项目真实 jar/dll 查证即将使用的 SDK 类、方法、构造器或属性签名;成功生成 evidence/sdk-signature.md 后再写代码。禁止凭记忆或随包知识库猜 SDK API。`;
118
154
  }
155
+
156
+ export function dataSourceWriteBlockedReason(path: string, evidenceName: string): string {
157
+ return `不能写生产源码 ${path}:缺少真实数据源/元数据证据 ${evidenceName}。下一步:确认目标 FormId、单据/表单、字段/实体、表或数据来源和测试数据;API 文档只能证明调用方式,不能证明当前业务数据源存在。`;
158
+ }
159
+
160
+ export function integrationWriteBlockedReason(path: string, missing: string[]): string {
161
+ return `不能写生产源码 ${path}:第三方对接上下文不完整,缺少 ${missing.join("、")}。下一步:补齐 ${fieldLabels(INTEGRATION_CONTEXT_FIELDS).join("、")}。`;
162
+ }
163
+
164
+ export function implementationWriteBlockedReason(path: string, missing: string[]): string {
165
+ return `不能写生产源码 ${path}:实现就绪信息不完整,缺少 ${missing.join("、")}。下一步:确认 ${fieldLabels(IMPLEMENTATION_CONTRACT_FIELDS).join("、")};信息不足时继续提问,禁止生成模板代码。`;
166
+ }
167
+
168
+ function nextQuestionInstruction(missing: string[]): string | undefined {
169
+ const question = questionForMissingLabel(missing[0]);
170
+ if (!question) return undefined;
171
+ return `阻断问题命令:kd_question action=ask factLabel="${missing[0]}" question="${question}" reason="${missing[0]} 缺失会阻塞实现。"。`;
172
+ }
@@ -18,6 +18,7 @@ const SOURCE_EXTENSIONS = new Set([
18
18
  ".yaml",
19
19
  ".sql",
20
20
  ".ksql",
21
+ ".cs",
21
22
  ".py",
22
23
  ]);
23
24
 
@@ -22,12 +22,12 @@ export function parsePlanSteps(plan: string): PlanStep[] {
22
22
 
23
23
  export function planStepsBlockReason(plan: string): string | undefined {
24
24
  if (!/##\s*执行步骤/i.test(plan)) {
25
- return "PLAN.md 缺少 ## 执行步骤。下一步:回到 plan,补充 `## 执行步骤`,把工作拆成 `- [ ] STEP-001:...` 这种可跟踪步骤;每一步都应能产生代码变更或 evidence。";
25
+ return "PLAN.md 缺少 ## 执行步骤。下一步:回到 plan,补充 `## 执行步骤`;使用 `- [ ] STEP-001:...` 拆分可跟踪步骤;每一步必须产生代码变更或 evidence。";
26
26
  }
27
27
 
28
28
  const steps = parsePlanSteps(plan);
29
29
  if (steps.length === 0) {
30
- return "PLAN.md 没有可执行步骤。下一步:在 `## 执行步骤` 下使用 `- [ ] STEP-001:...` 列出步骤;不要用普通段落代替可勾选步骤。";
30
+ return "PLAN.md 没有可执行步骤。下一步:在 `## 执行步骤` 下使用 `- [ ] STEP-001:...` 列出步骤;禁止用普通段落代替可勾选步骤。";
31
31
  }
32
32
 
33
33
  const duplicate = firstDuplicate(steps.map((step) => step.id));
@@ -56,7 +56,7 @@ export function executionStepsBlockReason(cwd: string, run: ActiveRun, plan: str
56
56
  }
57
57
 
58
58
  if (missing.length > 0) {
59
- return `不能进入 verify:EXECUTION.md 未完成计划步骤 ${missing.join(", ")}。下一步:继续执行这些步骤,完成后在 EXECUTION.md 的 ## 步骤结果 中用 \`- [x] STEP-###:已完成。证据:evidence/step-###.md\` 记录;未真正执行时不要勾选。`;
59
+ return `不能进入 verify:EXECUTION.md 未完成计划步骤 ${missing.join(", ")}。下一步:执行缺失步骤;完成后在 EXECUTION.md 的 ## 步骤结果 中使用 \`- [x] STEP-###:已完成。证据:evidence/step-###.md\` 记录;未执行完成时禁止勾选。`;
60
60
  }
61
61
  if (missingEvidence.length > 0) {
62
62
  return `不能进入 verify:步骤 ${missingEvidence.join(", ")} 缺少已落地的 evidence 文件。下一步:为每个步骤创建真实 evidence 文件,写入检查命令、结果、改动文件或验证输出,然后在 EXECUTION.md 对应步骤行引用该 evidence 路径。`;
@@ -0,0 +1,227 @@
1
+ import type { KdPhase } from "./types.ts";
2
+
3
+ export interface ContractField {
4
+ id: string;
5
+ label: string;
6
+ aliases: string[];
7
+ question: string;
8
+ }
9
+
10
+ export const IMPLEMENTATION_CONTRACT_FIELDS: ContractField[] = [
11
+ {
12
+ id: "trigger",
13
+ label: "触发入口或执行时机",
14
+ aliases: ["触发入口", "执行时机", "触发时机", "插件类型和事件", "入口事件"],
15
+ question: "该实现由哪个入口触发,执行时机是什么?",
16
+ },
17
+ {
18
+ id: "source",
19
+ label: "源对象/输入数据",
20
+ aliases: ["源对象", "源单", "输入数据", "数据来源", "取数来源"],
21
+ question: "实现读取的源对象或输入数据是什么?",
22
+ },
23
+ {
24
+ id: "target",
25
+ label: "目标对象/输出结果",
26
+ aliases: ["目标对象", "目标单据", "目标表单", "输出结果", "生成结果"],
27
+ question: "实现写入、生成或影响的目标对象是什么?",
28
+ },
29
+ {
30
+ id: "data-change",
31
+ label: "数据变化或字段映射",
32
+ aliases: ["数据变化", "字段映射", "字段对应", "字段改写", "赋值规则"],
33
+ question: "列出新增、修改、映射或保持不变的字段。",
34
+ },
35
+ {
36
+ id: "rules",
37
+ label: "业务规则和适用条件",
38
+ aliases: ["业务规则", "适用条件", "执行条件", "过滤条件", "前置条件"],
39
+ question: "该实现在哪些条件下执行,业务规则是什么?",
40
+ },
41
+ {
42
+ id: "failure",
43
+ label: "失败处理、回滚或人工处理方式",
44
+ aliases: ["失败处理", "异常处理", "错误处理", "回滚", "补偿", "人工处理"],
45
+ question: "失败、异常或重复执行时如何处理?",
46
+ },
47
+ {
48
+ id: "acceptance",
49
+ label: "验收样例或测试数据",
50
+ aliases: ["验收样例", "测试数据", "请求样例", "响应样例", "示例数据"],
51
+ question: "用于验收的样例数据和预期结果是什么?",
52
+ },
53
+ ];
54
+
55
+ export const DATA_SOURCE_CONTEXT_FIELDS: ContractField[] = [
56
+ {
57
+ id: "target-form",
58
+ label: "目标 FormId/单据或表单标识",
59
+ aliases: ["目标 FormId", "FormId", "Form ID", "form id", "表单标识", "单据标识", "目标单据", "目标表单"],
60
+ question: "目标 FormId、单据或表单标识是什么?",
61
+ },
62
+ {
63
+ id: "plugin-hook",
64
+ label: "插件类型和触发事件",
65
+ aliases: ["插件类型", "触发事件", "生命周期", "挂载点", "插件类型和事件"],
66
+ question: "插件类型和触发事件是什么?",
67
+ },
68
+ {
69
+ id: "field-entity",
70
+ label: "字段/实体/分录标识",
71
+ aliases: ["字段标识", "实体标识", "分录标识", "单据体标识", "字段/实体标识"],
72
+ question: "涉及哪些字段、实体或分录标识?",
73
+ },
74
+ {
75
+ id: "data-access",
76
+ label: "数据读取写入方式",
77
+ aliases: ["数据读取写入方式", "读取方式", "写入方式", "数据访问", "取数方式"],
78
+ question: "数据通过表单模型、服务、SQL/KSQL 还是接口读取和写入?",
79
+ },
80
+ {
81
+ id: "sql-identifiers",
82
+ label: "SQL/KSQL 表名和数据库字段名",
83
+ aliases: ["SQL 表名", "KSQL 表名", "表名", "数据库字段名", "字段名"],
84
+ question: "SQL/KSQL 场景的表名和数据库字段名是什么?",
85
+ },
86
+ ];
87
+
88
+ export const INTEGRATION_CONTEXT_FIELDS: ContractField[] = [
89
+ {
90
+ id: "interface-doc",
91
+ label: "接口文档来源/版本",
92
+ aliases: ["接口文档", "API 文档", "文档来源", "协议文档", "OpenAPI", "Swagger"],
93
+ question: "第三方接口文档来源和版本是什么?",
94
+ },
95
+ {
96
+ id: "direction",
97
+ label: "对接方向和触发时机",
98
+ aliases: ["对接方向", "触发时机", "同步方向", "数据流向"],
99
+ question: "对接方向、调用方和触发时机是什么?",
100
+ },
101
+ {
102
+ id: "endpoint-auth",
103
+ label: "接口地址、认证和密钥配置方式",
104
+ aliases: ["接口地址", "认证", "鉴权", "密钥", "Token", "OAuth"],
105
+ question: "接口地址、认证方式和密钥配置方式是什么?",
106
+ },
107
+ {
108
+ id: "field-mapping",
109
+ label: "第三方字段到金蝶字段的映射",
110
+ aliases: ["字段映射", "映射关系", "字段对应", "字段对照"],
111
+ question: "第三方字段与金蝶字段如何对应?",
112
+ },
113
+ {
114
+ id: "concurrency",
115
+ label: "并发/幂等策略",
116
+ aliases: ["并发", "幂等", "去重", "唯一键", "重复提交"],
117
+ question: "并发、重复提交和幂等如何处理?",
118
+ },
119
+ {
120
+ id: "retry",
121
+ label: "超时、重试和限流策略",
122
+ aliases: ["超时", "重试", "限流", "频率", "熔断"],
123
+ question: "超时、重试和限流策略是什么?",
124
+ },
125
+ {
126
+ id: "error",
127
+ label: "错误处理和失败补偿",
128
+ aliases: ["错误处理", "异常处理", "失败处理", "失败补偿", "告警"],
129
+ question: "接口失败、异常响应和补偿流程如何处理?",
130
+ },
131
+ {
132
+ id: "logging",
133
+ label: "日志、审计和敏感信息脱敏策略",
134
+ aliases: ["日志", "审计", "留痕", "脱敏", "敏感信息"],
135
+ question: "列出日志字段、审计留痕和敏感信息脱敏规则。",
136
+ },
137
+ {
138
+ id: "samples",
139
+ label: "请求/响应样例和验收数据",
140
+ aliases: ["请求样例", "响应样例", "报文样例", "验收数据", "payload"],
141
+ question: "请求样例、响应样例和验收数据是什么?",
142
+ },
143
+ ];
144
+
145
+ export const PROJECT_PERSISTENT_RULES = [
146
+ "计划或编辑代码前必须读取本文件;本文件过期时先运行 `kcode context --refresh`。",
147
+ "信息不足时禁止开始编码。必须先登记一个最阻塞的结构化问题,获得可核验答案后再继续;禁止输出 demo/sample/scaffold、模板代码或占位实现。",
148
+ "API 文档、SDK 文档和知识库只能证明技术用法,不能替代业务事实。FormId、单据/表单标识、字段/实体/分录标识、插件类型与事件、SQL/KSQL 表名和数据库字段名必须来自用户确认、项目元数据或 evidence。",
149
+ `产品代码实现前必须具备通用实现契约:${IMPLEMENTATION_CONTRACT_FIELDS.map((field) => field.label).join("、")}。`,
150
+ `涉及业务数据源时必须具备数据源上下文:${DATA_SOURCE_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
151
+ `涉及第三方对接时必须具备接口与运行上下文:${INTEGRATION_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
152
+ "内部插件、自动下推、字段改写、数据同步等需求不得按场景写死提示词;统一通过实现契约、数据源上下文、第三方对接上下文补齐事实。",
153
+ "PLAN 自由文本不能单独证明 FormId、字段、插件事件或读写方式;门禁只信任结构化 facts 和 evidence。",
154
+ "`run.facts` 是唯一结构化事实源;已回答 questions 仅为审计记录,禁止在读取状态时从历史 question 反推事实。",
155
+ "`factLabel` 必须使用集中定义的事实标签或别名;未知标签、占位答案、口头确认语、待确认/TODO/TBD/按实际环境等不能解除门禁。",
156
+ "用户回答 open question 后,先用 `kd_question action=answer` 写入答案;用户更正事实时用 `kd_question action=revise`,禁止重复询问已确认事实。",
157
+ "企业版 Python 插件通常没有本地构建可替代验证;BOS 注册、外部系统操作、人工功能测试和生产环境验证必须由用户提供可核验证据并记录来源。",
158
+ "提示语集中管理为正式工程指令;禁止口语化、闲聊式、鼓励式提示词进入运行时规则。",
159
+ "不做旧状态迁移兼容或旧问题答案推断;坏状态只过滤,缺失事实必须重新提问确认。",
160
+ ];
161
+
162
+ export const PROMPT_STYLE_RULES = [
163
+ "使用正式、可执行的工程指令;禁止口语化、闲聊式、鼓励式表达。",
164
+ "事实不足时生成阻断问题;禁止输出模板代码、占位实现或基于猜测的业务标识。",
165
+ "每次只提出一个最阻塞问题;问题必须指向可验证事实、数据标识或验收证据。",
166
+ "引用顺序:当前项目文件、PLAN/SPEC、元数据 evidence、SDK 签名、验证输出。",
167
+ ];
168
+
169
+ export const CORE_WORKFLOW_CONSTRAINTS = [
170
+ "产品代码只在 execute 阶段写入,并限于 PLAN.md 批准的文件。",
171
+ "写代码前必须具备通用实现契约:触发入口、源对象/输入数据、目标对象/输出结果、数据变化或字段映射、业务规则、失败处理和验收样例。",
172
+ "业务数据源未知时禁止编码;确认目标 FormId/单据或表单、插件类型和事件、字段/实体/分录标识、数据读取写入方式后再编码;SQL/KSQL 同步确认表名和数据库字段名。",
173
+ "第三方对接确认接口文档、对接方向、触发时机、认证配置、字段映射、并发/幂等、重试超时限流、错误补偿、日志脱敏和验收样例后再编码。",
174
+ "事实缺失时使用 kd_question 登记一个最阻塞问题;禁止用 API 文档、SDK 知识库或推测替代业务事实。",
175
+ "用户输入是在回答 open question 时,必须先调用 kd_question action=answer 记录答案,再继续推进或登记下一个问题。",
176
+ "同一 factLabel 已有当前事实时禁止重复提问;用户明确更正时使用 kd_question action=revise 记录新事实和更正原因。",
177
+ "run.facts 是唯一结构化事实源;questions 仅作为问答审计记录,禁止从历史 question 反推门禁事实。",
178
+ "待确认、未知、按实际环境、TODO/TBD 等占位答案不能解除门禁。",
179
+ "Java/C# SDK 签名以当前项目 jar/dll、构建输出或官方元数据为准。",
180
+ "Java/Cosmic 使用当前项目 Gradle;C#/企业版使用 dotnet build。",
181
+ "evidence 必须记录命令、Exit 和关键输出;命令无法运行时记录阻塞原因。",
182
+ "外部系统操作、BOS 注册、人工功能测试和生产环境验证不能由 LLM 代办;必须要求用户提供验证结果或可核验证据,并记录证据来源。",
183
+ "Windows 路径规则:项目相对路径为默认;绝对路径使用 D:\\... 形式。",
184
+ ];
185
+
186
+ export const PHASE_GUIDANCE: Record<KdPhase, string> = {
187
+ discuss: "梳理需求来源、范围、已知事实;如缺通用实现契约、数据源或第三方接口关键事实,使用 kd_question 登记一个最阻塞问题。",
188
+ spec: "将需求转成验收标准、实现契约、数据对象、接口契约、异常行为、依赖和风险;数据对象和字段映射必须落到可核验标识。",
189
+ plan: "检查项目结构,写明目标路径、允许修改文件、通用实现契约、数据源/元数据查证项、第三方接口契约、插件挂载点、验证命令和回滚说明。",
190
+ execute: "按 PLAN.md 实现,记录步骤结果、变更文件和 evidence。",
191
+ verify: "运行计划中的验证命令,并用 kd_verify_result 记录结果;失败会回到 execute 修复,成功后更新 VERIFY.md、证据和残余风险。",
192
+ ship: "整理 SHIP.md,包括摘要、验证证据、风险和后续事项。",
193
+ };
194
+
195
+ export const PLAN_REQUIRED_CHECK_LINES = [
196
+ "涉及表单、单据、字段、实体、SQL/KSQL、数据读取写入或插件事件时,确认真实数据源/元数据后再编码;禁止只根据 API 文档编码。",
197
+ "Cosmic 家族数据源证据:evidence/cosmic-metadata.json;企业版/BOS 数据源证据:evidence/data-source.md。",
198
+ `所有产品代码实现必须写明通用实现契约后再编码:${IMPLEMENTATION_CONTRACT_FIELDS.map((field) => field.label).join("、")}。`,
199
+ `进入 execute 前必须写明:${DATA_SOURCE_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
200
+ `第三方对接必须写明:${INTEGRATION_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
201
+ "Java/C# 代码涉及 SDK 类、方法、构造器、枚举、属性时,使用 kd_sdk_signature 或项目构建输出确认真实签名后再编码。",
202
+ "知识库搜索、随包 Cosmic API 查询只能作为线索,不能作为最终方法签名事实。",
203
+ "SDK 签名证据:evidence/sdk-signature.md",
204
+ ];
205
+
206
+ export function formatPromptLines(lines: string[]): string[] {
207
+ return lines.map((line) => `- ${line}`);
208
+ }
209
+
210
+ export function fieldLabels(fields: ContractField[]): string[] {
211
+ return fields.map((field) => field.label);
212
+ }
213
+
214
+ export function questionForMissingLabel(label: string): string | undefined {
215
+ const fields = [...IMPLEMENTATION_CONTRACT_FIELDS, ...DATA_SOURCE_CONTEXT_FIELDS, ...INTEGRATION_CONTEXT_FIELDS];
216
+ return fields.find((field) => field.label === label)?.question;
217
+ }
218
+
219
+ export function canonicalFactLabel(label: string): string | undefined {
220
+ const normalized = normalizeLabel(label);
221
+ const fields = [...IMPLEMENTATION_CONTRACT_FIELDS, ...DATA_SOURCE_CONTEXT_FIELDS, ...INTEGRATION_CONTEXT_FIELDS];
222
+ return fields.find((field) => [field.label, ...field.aliases].some((item) => normalizeLabel(item) === normalized))?.label;
223
+ }
224
+
225
+ function normalizeLabel(label: string): string {
226
+ return label.trim().toLowerCase().replace(/\s+/g, "");
227
+ }
@@ -4,6 +4,8 @@ import { delegationGuidanceForWorkflow, shouldInjectDelegationGuidance } from ".
4
4
  import { formatStatus } from "./format.ts";
5
5
  import type { ActiveRun, KdPhase } from "./types.ts";
6
6
  import { PHASE_ORDER } from "./types.ts";
7
+ import { CORE_WORKFLOW_CONSTRAINTS, PHASE_GUIDANCE, PROMPT_STYLE_RULES, formatPromptLines } from "./prompt-policy.ts";
8
+ import { formatQuestionMemory } from "./question-memory.ts";
7
9
 
8
10
  export function workflowPromptForRun(cwd: string, run: ActiveRun, userText: string): string {
9
11
  const status = formatStatus(cwd, run);
@@ -22,18 +24,20 @@ export function workflowPromptForRun(cwd: string, run: ActiveRun, userText: stri
22
24
  "KCode 阶段资料:",
23
25
  memory,
24
26
  "",
27
+ "KCode 已问已答事实:",
28
+ formatQuestionMemory(run),
29
+ "",
25
30
  "项目上下文:",
26
- projectContext ? trimForPrompt(projectContext, 1200) : "未生成。需要项目结构时先运行或提示用户运行 `kcode context --refresh`。",
31
+ projectContext ? trimForPrompt(projectContext, 1200) : "未生成。项目结构缺失时运行 `kcode context --refresh`。",
27
32
  "",
28
33
  "当前阶段任务:",
29
34
  phaseGuidance,
30
35
  "",
36
+ "工程指令风格:",
37
+ ...formatPromptLines(PROMPT_STYLE_RULES),
38
+ "",
31
39
  "核心约束:",
32
- "- 产品代码只在 execute 阶段写入,并限于 PLAN.md 批准的文件。",
33
- "- Java/C# SDK 签名以当前项目 jar/dll、构建输出或官方元数据为准。",
34
- "- Java/Cosmic 用当前项目 Gradle;C#/企业版用 dotnet build。",
35
- "- evidence 记录命令、Exit 和关键输出;命令无法运行时记录阻塞原因。",
36
- "- Windows 下优先使用项目相对路径;绝对路径使用 D:\\... 形式。",
40
+ ...formatPromptLines(CORE_WORKFLOW_CONSTRAINTS),
37
41
  ...delegationGuidance,
38
42
  ].join("\n");
39
43
  }
@@ -48,15 +52,7 @@ export function repairPromptForRun(run: ActiveRun): string {
48
52
  }
49
53
 
50
54
  function phaseGuidanceForRun(phase: KdPhase): string {
51
- const guidance: Record<KdPhase, string> = {
52
- discuss: "梳理需求来源、范围、已知事实和一个最阻塞的待确认问题。",
53
- spec: "把需求转成验收标准、数据对象、异常行为、依赖和风险。",
54
- plan: "检查项目结构,写明目标路径、允许修改文件、查证项、验证命令和回滚说明。",
55
- execute: "按 PLAN.md 实现,记录步骤结果、变更文件和 evidence。",
56
- verify: "运行计划中的验证命令,并用 kd_verify_result 记录结果;失败会回到 execute 修复,成功后更新 VERIFY.md、证据和残余风险。",
57
- ship: "整理 SHIP.md,包括摘要、验证证据、风险和后续事项。",
58
- };
59
- return guidance[phase];
55
+ return PHASE_GUIDANCE[phase];
60
56
  }
61
57
 
62
58
  function workflowMemoryForRun(cwd: string, run: ActiveRun): string {
@@ -76,5 +72,5 @@ function workflowMemoryForRun(cwd: string, run: ActiveRun): string {
76
72
 
77
73
  function trimForPrompt(content: string, maxLength: number): string {
78
74
  if (content.length <= maxLength) return content;
79
- return `${content.slice(0, maxLength)}\n\n[...已截断;需要完整内容时读取本地文件...]`;
75
+ return `${content.slice(0, maxLength)}\n\n[...已截断;完整内容读取本地文件...]`;
80
76
  }