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.
- package/README.md +10 -10
- package/dist/cli/kcode.js +3 -3
- package/dist/context/project-context.js +4 -5
- package/dist/harness/prompt-policy.d.ts +19 -0
- package/dist/harness/prompt-policy.js +206 -0
- package/dist/harness/types.d.ts +74 -0
- package/dist/harness/types.js +16 -0
- package/dist/product/profile.d.ts +20 -0
- package/dist/product/profile.js +103 -0
- package/docs/CHANGELOG.md +90 -2
- package/docs/COMMAND_REFERENCE.md +27 -12
- package/docs/DEVELOPMENT.md +3 -3
- package/docs/EVIDENCE_AND_GATES.md +15 -8
- package/docs/HARNESS_WORKFLOW.md +32 -12
- package/docs/KCODE_DISTRIBUTION.md +7 -7
- package/docs/PRODUCT_PROFILE.md +9 -9
- package/docs/TROUBLESHOOTING.md +8 -8
- package/docs/USER_GUIDE.md +10 -10
- package/extensions/kingdee-harness.ts +141 -86
- package/extensions/kingdee-header.ts +1 -1
- package/extensions/kingdee-subagents.ts +1 -1
- package/extensions/kingdee-tools.ts +44 -44
- package/package.json +1 -1
- package/src/cli/kcode.ts +3 -3
- package/src/context/project-context.ts +4 -5
- package/src/harness/artifacts.ts +6 -7
- package/src/harness/data-source-policy.ts +346 -0
- package/src/harness/delegation.ts +28 -23
- package/src/harness/gates.ts +16 -1
- package/src/harness/messages.ts +65 -11
- package/src/harness/path-policy.ts +1 -0
- package/src/harness/plan-steps.ts +3 -3
- package/src/harness/prompt-policy.ts +227 -0
- package/src/harness/prompt.ts +12 -16
- package/src/harness/question-memory.ts +220 -0
- package/src/harness/repair.ts +18 -3
- package/src/harness/state.ts +93 -6
- package/src/harness/types.ts +19 -0
- package/src/official/kingdee-skills.ts +4 -4
- package/src/product/profile.ts +2 -2
- package/src/rules/checker.ts +27 -27
- package/src/tools/build-debug.ts +5 -5
- package/src/tools/sdk-signature.ts +4 -4
package/README.md
CHANGED
|
@@ -6,12 +6,12 @@ KCode 默认面向中文用户。命令名和产品标识保持英文,例如 `
|
|
|
6
6
|
|
|
7
7
|
## 文档导航
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
首次使用按顺序阅读:
|
|
10
10
|
|
|
11
11
|
1. [用户指南](docs/USER_GUIDE.md):安装、初始化、启动、模型配置、升级。
|
|
12
12
|
2. [Harness 工作流](docs/HARNESS_WORKFLOW.md):如何从需求进入 discuss -> spec -> plan -> execute -> verify -> ship。
|
|
13
13
|
3. [产品画像确认](docs/PRODUCT_PROFILE.md):如何用 `/kd-product` 确认苍穹、星瀚、星空旗舰版或企业版。
|
|
14
|
-
4. [证据和门禁](docs/EVIDENCE_AND_GATES.md)
|
|
14
|
+
4. [证据和门禁](docs/EVIDENCE_AND_GATES.md):evidence、SDK 签名、TDD 红绿证据和风险确认规则。
|
|
15
15
|
5. [命令参考](docs/COMMAND_REFERENCE.md):完整 `kcode` 子命令、Pi 内 `/kd-*` 命令和内置工具参数。
|
|
16
16
|
6. [更新日志](docs/CHANGELOG.md):查看版本变化、发布验证和近期改进。
|
|
17
17
|
7. [故障排查](docs/TROUBLESHOOTING.md):模型、旧版本、Windows 路径、终端显示等常见问题。
|
|
@@ -28,7 +28,7 @@ KCode 默认面向中文用户。命令名和产品标识保持英文,例如 `
|
|
|
28
28
|
|
|
29
29
|
- Node.js `>=22.19.0`
|
|
30
30
|
- npm
|
|
31
|
-
- Windows
|
|
31
|
+
- Windows 环境使用 Windows Terminal
|
|
32
32
|
|
|
33
33
|
安装:
|
|
34
34
|
|
|
@@ -44,7 +44,7 @@ kcode doctor --deep
|
|
|
44
44
|
kcode start
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
首次启动出现无模型信息时,在 KCode/Pi 中执行:
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
50
|
/login
|
|
@@ -53,19 +53,19 @@ kcode start
|
|
|
53
53
|
|
|
54
54
|
## 第一个需求
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
使用显式产品参数:
|
|
57
57
|
|
|
58
58
|
```text
|
|
59
59
|
/kd-start --product cangqiong 实现采购订单保存校验
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
支持直接提供需求文档:
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
/kd-start --product cangqiong E:\project\docs\需求说明.md
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
已创建 run 且标题栏显示产品未确认时:
|
|
69
69
|
|
|
70
70
|
```text
|
|
71
71
|
/kd-product cangqiong --version V6.0
|
|
@@ -82,7 +82,7 @@ flagship 星空旗舰版 / 基于苍穹/Cosmic
|
|
|
82
82
|
enterprise 金蝶企业版 / C#
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
进入 `ship`
|
|
85
|
+
进入 `ship` 前必须确认风险:
|
|
86
86
|
|
|
87
87
|
```text
|
|
88
88
|
/kd-risk low 已完成本地构建和元数据检查,无残余交付风险
|
|
@@ -156,7 +156,7 @@ KCode 只维护当前业务项目内的配置和工作流状态:
|
|
|
156
156
|
.pi/kd/runs/<run-id>/evidence/index.json
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
KCode
|
|
159
|
+
KCode 无需单独安装全局 `pi` 命令,也不会修改用户全局 Pi 配置。
|
|
160
160
|
|
|
161
161
|
## 升级
|
|
162
162
|
|
|
@@ -165,7 +165,7 @@ npm install -g kcode-pi@latest
|
|
|
165
165
|
kcode version
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
升级后在业务项目根目录重新执行:
|
|
169
169
|
|
|
170
170
|
```powershell
|
|
171
171
|
kcode init
|
package/dist/cli/kcode.js
CHANGED
|
@@ -71,8 +71,8 @@ export function doctor(cwd, args = []) {
|
|
|
71
71
|
lines.push(`Pi CLI:${formatPiCliStatus(piCli, pi)}`);
|
|
72
72
|
lines.push(`KCode version:${packageName}@${packageVersion}`);
|
|
73
73
|
lines.push(`KCode package:${packageRoot}`);
|
|
74
|
-
lines.push(`项目配置:${existsSync(settingsPath) ? settingsPath : "
|
|
75
|
-
lines.push(`项目上下文:${existsSync(projectContextPath) ? projectContextPath : "
|
|
74
|
+
lines.push(`项目配置:${existsSync(settingsPath) ? settingsPath : "未创建,运行 kcode init"}`);
|
|
75
|
+
lines.push(`项目上下文:${existsSync(projectContextPath) ? projectContextPath : "未创建,运行 kcode context"}`);
|
|
76
76
|
if (existsSync(settingsPath)) {
|
|
77
77
|
const settingsResult = readSettingsSafe(settingsPath);
|
|
78
78
|
if (!settingsResult.ok) {
|
|
@@ -166,7 +166,7 @@ export function start(cwd, piArgs) {
|
|
|
166
166
|
if (!piCli) {
|
|
167
167
|
return {
|
|
168
168
|
exitCode: 1,
|
|
169
|
-
output: `${init.output}\n未找到随包 Pi CLI 或全局 pi
|
|
169
|
+
output: `${init.output}\n未找到随包 Pi CLI 或全局 pi 命令。重新安装 kcode-pi 后再运行 kcode start。`,
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
const pi = spawnSync(piCli.command, piCli.args, { cwd, stdio: "inherit", shell: false });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, extname, join, relative } from "node:path";
|
|
3
|
+
import { PROJECT_PERSISTENT_RULES, formatPromptLines } from "../harness/prompt-policy.js";
|
|
3
4
|
const IGNORED_DIRS = new Set([
|
|
4
5
|
".git",
|
|
5
6
|
".idea",
|
|
@@ -55,11 +56,9 @@ export function generateProjectContext(cwd) {
|
|
|
55
56
|
"",
|
|
56
57
|
"## 持久规则",
|
|
57
58
|
"",
|
|
58
|
-
|
|
59
|
-
"-
|
|
60
|
-
"-
|
|
61
|
-
"- 调用文件工具时优先使用项目相对路径。在 Windows 中,不要把路径改写为 /mnt/<drive>/... 或 /<drive>/...;只有确需绝对路径时才使用 Windows 路径。",
|
|
62
|
-
"- 如果本文件过期,计划前先运行 `kcode context --refresh` 重新生成。",
|
|
59
|
+
...formatPromptLines(PROJECT_PERSISTENT_RULES),
|
|
60
|
+
"- 禁止假设模块结构。必须基于下方真实路径,并在编辑前确认目标文件。",
|
|
61
|
+
"- 调用文件工具时默认使用项目相对路径。在 Windows 中禁止把路径改写为 /mnt/<drive>/... 或 /<drive>/...;绝对路径只允许使用 Windows 路径。",
|
|
63
62
|
"- 只有 Harness 进入 `execute` 且 PLAN.md 写明真实目标路径后,才能写产品代码。",
|
|
64
63
|
"",
|
|
65
64
|
"## 项目结构摘要",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { KdPhase } from "./types.ts";
|
|
2
|
+
export interface ContractField {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
aliases: string[];
|
|
6
|
+
question: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const IMPLEMENTATION_CONTRACT_FIELDS: ContractField[];
|
|
9
|
+
export declare const DATA_SOURCE_CONTEXT_FIELDS: ContractField[];
|
|
10
|
+
export declare const INTEGRATION_CONTEXT_FIELDS: ContractField[];
|
|
11
|
+
export declare const PROJECT_PERSISTENT_RULES: string[];
|
|
12
|
+
export declare const PROMPT_STYLE_RULES: string[];
|
|
13
|
+
export declare const CORE_WORKFLOW_CONSTRAINTS: string[];
|
|
14
|
+
export declare const PHASE_GUIDANCE: Record<KdPhase, string>;
|
|
15
|
+
export declare const PLAN_REQUIRED_CHECK_LINES: string[];
|
|
16
|
+
export declare function formatPromptLines(lines: string[]): string[];
|
|
17
|
+
export declare function fieldLabels(fields: ContractField[]): string[];
|
|
18
|
+
export declare function questionForMissingLabel(label: string): string | undefined;
|
|
19
|
+
export declare function canonicalFactLabel(label: string): string | undefined;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
export const IMPLEMENTATION_CONTRACT_FIELDS = [
|
|
2
|
+
{
|
|
3
|
+
id: "trigger",
|
|
4
|
+
label: "触发入口或执行时机",
|
|
5
|
+
aliases: ["触发入口", "执行时机", "触发时机", "插件类型和事件", "入口事件"],
|
|
6
|
+
question: "该实现由哪个入口触发,执行时机是什么?",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "source",
|
|
10
|
+
label: "源对象/输入数据",
|
|
11
|
+
aliases: ["源对象", "源单", "输入数据", "数据来源", "取数来源"],
|
|
12
|
+
question: "实现读取的源对象或输入数据是什么?",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "target",
|
|
16
|
+
label: "目标对象/输出结果",
|
|
17
|
+
aliases: ["目标对象", "目标单据", "目标表单", "输出结果", "生成结果"],
|
|
18
|
+
question: "实现写入、生成或影响的目标对象是什么?",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "data-change",
|
|
22
|
+
label: "数据变化或字段映射",
|
|
23
|
+
aliases: ["数据变化", "字段映射", "字段对应", "字段改写", "赋值规则"],
|
|
24
|
+
question: "列出新增、修改、映射或保持不变的字段。",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "rules",
|
|
28
|
+
label: "业务规则和适用条件",
|
|
29
|
+
aliases: ["业务规则", "适用条件", "执行条件", "过滤条件", "前置条件"],
|
|
30
|
+
question: "该实现在哪些条件下执行,业务规则是什么?",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "failure",
|
|
34
|
+
label: "失败处理、回滚或人工处理方式",
|
|
35
|
+
aliases: ["失败处理", "异常处理", "错误处理", "回滚", "补偿", "人工处理"],
|
|
36
|
+
question: "失败、异常或重复执行时如何处理?",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "acceptance",
|
|
40
|
+
label: "验收样例或测试数据",
|
|
41
|
+
aliases: ["验收样例", "测试数据", "请求样例", "响应样例", "示例数据"],
|
|
42
|
+
question: "用于验收的样例数据和预期结果是什么?",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
export const DATA_SOURCE_CONTEXT_FIELDS = [
|
|
46
|
+
{
|
|
47
|
+
id: "target-form",
|
|
48
|
+
label: "目标 FormId/单据或表单标识",
|
|
49
|
+
aliases: ["目标 FormId", "FormId", "Form ID", "form id", "表单标识", "单据标识", "目标单据", "目标表单"],
|
|
50
|
+
question: "目标 FormId、单据或表单标识是什么?",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "plugin-hook",
|
|
54
|
+
label: "插件类型和触发事件",
|
|
55
|
+
aliases: ["插件类型", "触发事件", "生命周期", "挂载点", "插件类型和事件"],
|
|
56
|
+
question: "插件类型和触发事件是什么?",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "field-entity",
|
|
60
|
+
label: "字段/实体/分录标识",
|
|
61
|
+
aliases: ["字段标识", "实体标识", "分录标识", "单据体标识", "字段/实体标识"],
|
|
62
|
+
question: "涉及哪些字段、实体或分录标识?",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "data-access",
|
|
66
|
+
label: "数据读取写入方式",
|
|
67
|
+
aliases: ["数据读取写入方式", "读取方式", "写入方式", "数据访问", "取数方式"],
|
|
68
|
+
question: "数据通过表单模型、服务、SQL/KSQL 还是接口读取和写入?",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "sql-identifiers",
|
|
72
|
+
label: "SQL/KSQL 表名和数据库字段名",
|
|
73
|
+
aliases: ["SQL 表名", "KSQL 表名", "表名", "数据库字段名", "字段名"],
|
|
74
|
+
question: "SQL/KSQL 场景的表名和数据库字段名是什么?",
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
export const INTEGRATION_CONTEXT_FIELDS = [
|
|
78
|
+
{
|
|
79
|
+
id: "interface-doc",
|
|
80
|
+
label: "接口文档来源/版本",
|
|
81
|
+
aliases: ["接口文档", "API 文档", "文档来源", "协议文档", "OpenAPI", "Swagger"],
|
|
82
|
+
question: "第三方接口文档来源和版本是什么?",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "direction",
|
|
86
|
+
label: "对接方向和触发时机",
|
|
87
|
+
aliases: ["对接方向", "触发时机", "同步方向", "数据流向"],
|
|
88
|
+
question: "对接方向、调用方和触发时机是什么?",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "endpoint-auth",
|
|
92
|
+
label: "接口地址、认证和密钥配置方式",
|
|
93
|
+
aliases: ["接口地址", "认证", "鉴权", "密钥", "Token", "OAuth"],
|
|
94
|
+
question: "接口地址、认证方式和密钥配置方式是什么?",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "field-mapping",
|
|
98
|
+
label: "第三方字段到金蝶字段的映射",
|
|
99
|
+
aliases: ["字段映射", "映射关系", "字段对应", "字段对照"],
|
|
100
|
+
question: "第三方字段与金蝶字段如何对应?",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "concurrency",
|
|
104
|
+
label: "并发/幂等策略",
|
|
105
|
+
aliases: ["并发", "幂等", "去重", "唯一键", "重复提交"],
|
|
106
|
+
question: "并发、重复提交和幂等如何处理?",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "retry",
|
|
110
|
+
label: "超时、重试和限流策略",
|
|
111
|
+
aliases: ["超时", "重试", "限流", "频率", "熔断"],
|
|
112
|
+
question: "超时、重试和限流策略是什么?",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "error",
|
|
116
|
+
label: "错误处理和失败补偿",
|
|
117
|
+
aliases: ["错误处理", "异常处理", "失败处理", "失败补偿", "告警"],
|
|
118
|
+
question: "接口失败、异常响应和补偿流程如何处理?",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "logging",
|
|
122
|
+
label: "日志、审计和敏感信息脱敏策略",
|
|
123
|
+
aliases: ["日志", "审计", "留痕", "脱敏", "敏感信息"],
|
|
124
|
+
question: "列出日志字段、审计留痕和敏感信息脱敏规则。",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "samples",
|
|
128
|
+
label: "请求/响应样例和验收数据",
|
|
129
|
+
aliases: ["请求样例", "响应样例", "报文样例", "验收数据", "payload"],
|
|
130
|
+
question: "请求样例、响应样例和验收数据是什么?",
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
export const PROJECT_PERSISTENT_RULES = [
|
|
134
|
+
"计划或编辑代码前必须读取本文件;本文件过期时先运行 `kcode context --refresh`。",
|
|
135
|
+
"信息不足时禁止开始编码。必须先登记一个最阻塞的结构化问题,获得可核验答案后再继续;禁止输出 demo/sample/scaffold、模板代码或占位实现。",
|
|
136
|
+
"API 文档、SDK 文档和知识库只能证明技术用法,不能替代业务事实。FormId、单据/表单标识、字段/实体/分录标识、插件类型与事件、SQL/KSQL 表名和数据库字段名必须来自用户确认、项目元数据或 evidence。",
|
|
137
|
+
`产品代码实现前必须具备通用实现契约:${IMPLEMENTATION_CONTRACT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
138
|
+
`涉及业务数据源时必须具备数据源上下文:${DATA_SOURCE_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
139
|
+
`涉及第三方对接时必须具备接口与运行上下文:${INTEGRATION_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
140
|
+
"内部插件、自动下推、字段改写、数据同步等需求不得按场景写死提示词;统一通过实现契约、数据源上下文、第三方对接上下文补齐事实。",
|
|
141
|
+
"PLAN 自由文本不能单独证明 FormId、字段、插件事件或读写方式;门禁只信任结构化 facts 和 evidence。",
|
|
142
|
+
"`run.facts` 是唯一结构化事实源;已回答 questions 仅为审计记录,禁止在读取状态时从历史 question 反推事实。",
|
|
143
|
+
"`factLabel` 必须使用集中定义的事实标签或别名;未知标签、占位答案、口头确认语、待确认/TODO/TBD/按实际环境等不能解除门禁。",
|
|
144
|
+
"用户回答 open question 后,先用 `kd_question action=answer` 写入答案;用户更正事实时用 `kd_question action=revise`,禁止重复询问已确认事实。",
|
|
145
|
+
"企业版 Python 插件通常没有本地构建可替代验证;BOS 注册、外部系统操作、人工功能测试和生产环境验证必须由用户提供可核验证据并记录来源。",
|
|
146
|
+
"提示语集中管理为正式工程指令;禁止口语化、闲聊式、鼓励式提示词进入运行时规则。",
|
|
147
|
+
"不做旧状态迁移兼容或旧问题答案推断;坏状态只过滤,缺失事实必须重新提问确认。",
|
|
148
|
+
];
|
|
149
|
+
export const PROMPT_STYLE_RULES = [
|
|
150
|
+
"使用正式、可执行的工程指令;禁止口语化、闲聊式、鼓励式表达。",
|
|
151
|
+
"事实不足时生成阻断问题;禁止输出模板代码、占位实现或基于猜测的业务标识。",
|
|
152
|
+
"每次只提出一个最阻塞问题;问题必须指向可验证事实、数据标识或验收证据。",
|
|
153
|
+
"引用顺序:当前项目文件、PLAN/SPEC、元数据 evidence、SDK 签名、验证输出。",
|
|
154
|
+
];
|
|
155
|
+
export const CORE_WORKFLOW_CONSTRAINTS = [
|
|
156
|
+
"产品代码只在 execute 阶段写入,并限于 PLAN.md 批准的文件。",
|
|
157
|
+
"写代码前必须具备通用实现契约:触发入口、源对象/输入数据、目标对象/输出结果、数据变化或字段映射、业务规则、失败处理和验收样例。",
|
|
158
|
+
"业务数据源未知时禁止编码;确认目标 FormId/单据或表单、插件类型和事件、字段/实体/分录标识、数据读取写入方式后再编码;SQL/KSQL 同步确认表名和数据库字段名。",
|
|
159
|
+
"第三方对接确认接口文档、对接方向、触发时机、认证配置、字段映射、并发/幂等、重试超时限流、错误补偿、日志脱敏和验收样例后再编码。",
|
|
160
|
+
"事实缺失时使用 kd_question 登记一个最阻塞问题;禁止用 API 文档、SDK 知识库或推测替代业务事实。",
|
|
161
|
+
"用户输入是在回答 open question 时,必须先调用 kd_question action=answer 记录答案,再继续推进或登记下一个问题。",
|
|
162
|
+
"同一 factLabel 已有当前事实时禁止重复提问;用户明确更正时使用 kd_question action=revise 记录新事实和更正原因。",
|
|
163
|
+
"run.facts 是唯一结构化事实源;questions 仅作为问答审计记录,禁止从历史 question 反推门禁事实。",
|
|
164
|
+
"待确认、未知、按实际环境、TODO/TBD 等占位答案不能解除门禁。",
|
|
165
|
+
"Java/C# SDK 签名以当前项目 jar/dll、构建输出或官方元数据为准。",
|
|
166
|
+
"Java/Cosmic 使用当前项目 Gradle;C#/企业版使用 dotnet build。",
|
|
167
|
+
"evidence 必须记录命令、Exit 和关键输出;命令无法运行时记录阻塞原因。",
|
|
168
|
+
"外部系统操作、BOS 注册、人工功能测试和生产环境验证不能由 LLM 代办;必须要求用户提供验证结果或可核验证据,并记录证据来源。",
|
|
169
|
+
"Windows 路径规则:项目相对路径为默认;绝对路径使用 D:\\... 形式。",
|
|
170
|
+
];
|
|
171
|
+
export const PHASE_GUIDANCE = {
|
|
172
|
+
discuss: "梳理需求来源、范围、已知事实;如缺通用实现契约、数据源或第三方接口关键事实,使用 kd_question 登记一个最阻塞问题。",
|
|
173
|
+
spec: "将需求转成验收标准、实现契约、数据对象、接口契约、异常行为、依赖和风险;数据对象和字段映射必须落到可核验标识。",
|
|
174
|
+
plan: "检查项目结构,写明目标路径、允许修改文件、通用实现契约、数据源/元数据查证项、第三方接口契约、插件挂载点、验证命令和回滚说明。",
|
|
175
|
+
execute: "按 PLAN.md 实现,记录步骤结果、变更文件和 evidence。",
|
|
176
|
+
verify: "运行计划中的验证命令,并用 kd_verify_result 记录结果;失败会回到 execute 修复,成功后更新 VERIFY.md、证据和残余风险。",
|
|
177
|
+
ship: "整理 SHIP.md,包括摘要、验证证据、风险和后续事项。",
|
|
178
|
+
};
|
|
179
|
+
export const PLAN_REQUIRED_CHECK_LINES = [
|
|
180
|
+
"涉及表单、单据、字段、实体、SQL/KSQL、数据读取写入或插件事件时,确认真实数据源/元数据后再编码;禁止只根据 API 文档编码。",
|
|
181
|
+
"Cosmic 家族数据源证据:evidence/cosmic-metadata.json;企业版/BOS 数据源证据:evidence/data-source.md。",
|
|
182
|
+
`所有产品代码实现必须写明通用实现契约后再编码:${IMPLEMENTATION_CONTRACT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
183
|
+
`进入 execute 前必须写明:${DATA_SOURCE_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
184
|
+
`第三方对接必须写明:${INTEGRATION_CONTEXT_FIELDS.map((field) => field.label).join("、")}。`,
|
|
185
|
+
"Java/C# 代码涉及 SDK 类、方法、构造器、枚举、属性时,使用 kd_sdk_signature 或项目构建输出确认真实签名后再编码。",
|
|
186
|
+
"知识库搜索、随包 Cosmic API 查询只能作为线索,不能作为最终方法签名事实。",
|
|
187
|
+
"SDK 签名证据:evidence/sdk-signature.md",
|
|
188
|
+
];
|
|
189
|
+
export function formatPromptLines(lines) {
|
|
190
|
+
return lines.map((line) => `- ${line}`);
|
|
191
|
+
}
|
|
192
|
+
export function fieldLabels(fields) {
|
|
193
|
+
return fields.map((field) => field.label);
|
|
194
|
+
}
|
|
195
|
+
export function questionForMissingLabel(label) {
|
|
196
|
+
const fields = [...IMPLEMENTATION_CONTRACT_FIELDS, ...DATA_SOURCE_CONTEXT_FIELDS, ...INTEGRATION_CONTEXT_FIELDS];
|
|
197
|
+
return fields.find((field) => field.label === label)?.question;
|
|
198
|
+
}
|
|
199
|
+
export function canonicalFactLabel(label) {
|
|
200
|
+
const normalized = normalizeLabel(label);
|
|
201
|
+
const fields = [...IMPLEMENTATION_CONTRACT_FIELDS, ...DATA_SOURCE_CONTEXT_FIELDS, ...INTEGRATION_CONTEXT_FIELDS];
|
|
202
|
+
return fields.find((field) => [field.label, ...field.aliases].some((item) => normalizeLabel(item) === normalized))?.label;
|
|
203
|
+
}
|
|
204
|
+
function normalizeLabel(label) {
|
|
205
|
+
return label.trim().toLowerCase().replace(/\s+/g, "");
|
|
206
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { KdProduct, ProductProfile } from "../product/profile.ts";
|
|
2
|
+
export type KdPhase = "discuss" | "spec" | "plan" | "execute" | "verify" | "ship";
|
|
3
|
+
export type KdRisk = "low" | "medium" | "high";
|
|
4
|
+
export type KdRunStatus = "active" | "paused" | "done";
|
|
5
|
+
export type KdRiskSource = "manual" | "verify" | "ship";
|
|
6
|
+
export interface RiskAssessment {
|
|
7
|
+
level: KdRisk;
|
|
8
|
+
reason: string;
|
|
9
|
+
source: KdRiskSource;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GateResult {
|
|
13
|
+
passed: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
checkedAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface RepairState {
|
|
18
|
+
attempts: number;
|
|
19
|
+
maxAttempts: number;
|
|
20
|
+
lastFailureEvidence?: string;
|
|
21
|
+
lastFailureSignature?: string;
|
|
22
|
+
status: "idle" | "repairing" | "blocked";
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
}
|
|
25
|
+
export interface KdQuestion {
|
|
26
|
+
id: string;
|
|
27
|
+
phase: KdPhase;
|
|
28
|
+
question: string;
|
|
29
|
+
reason?: string;
|
|
30
|
+
factLabel?: string;
|
|
31
|
+
proposedFactValue?: string;
|
|
32
|
+
choices?: string[];
|
|
33
|
+
blocking: boolean;
|
|
34
|
+
status: "open" | "answered";
|
|
35
|
+
answer?: string;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
answeredAt?: string;
|
|
38
|
+
}
|
|
39
|
+
export type KdFactStatus = "current" | "superseded" | "rejected";
|
|
40
|
+
export type KdFactSource = "question" | "manual";
|
|
41
|
+
export interface KdFact {
|
|
42
|
+
key: string;
|
|
43
|
+
label: string;
|
|
44
|
+
value: string;
|
|
45
|
+
status: KdFactStatus;
|
|
46
|
+
source: KdFactSource;
|
|
47
|
+
sourceQuestionId?: string;
|
|
48
|
+
reason?: string;
|
|
49
|
+
createdAt: string;
|
|
50
|
+
updatedAt: string;
|
|
51
|
+
supersededBy?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface ActiveRun {
|
|
54
|
+
id: string;
|
|
55
|
+
goal?: string;
|
|
56
|
+
phase: KdPhase;
|
|
57
|
+
cwd: string;
|
|
58
|
+
status?: KdRunStatus;
|
|
59
|
+
createdAt?: string;
|
|
60
|
+
updatedAt?: string;
|
|
61
|
+
product?: KdProduct;
|
|
62
|
+
version?: string;
|
|
63
|
+
profile?: ProductProfile;
|
|
64
|
+
riskAssessment?: RiskAssessment;
|
|
65
|
+
repair?: RepairState;
|
|
66
|
+
artifacts: Record<string, string>;
|
|
67
|
+
gate: GateResult;
|
|
68
|
+
questions?: KdQuestion[];
|
|
69
|
+
facts?: KdFact[];
|
|
70
|
+
}
|
|
71
|
+
export declare const PHASE_ORDER: KdPhase[];
|
|
72
|
+
export declare const PHASE_ARTIFACTS: Record<KdPhase, string>;
|
|
73
|
+
export declare function isKdPhase(value: string): value is KdPhase;
|
|
74
|
+
export declare function nextPhase(phase: KdPhase): KdPhase | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const PHASE_ORDER = ["discuss", "spec", "plan", "execute", "verify", "ship"];
|
|
2
|
+
export const PHASE_ARTIFACTS = {
|
|
3
|
+
discuss: "CONTEXT.md",
|
|
4
|
+
spec: "SPEC.md",
|
|
5
|
+
plan: "PLAN.md",
|
|
6
|
+
execute: "EXECUTION.md",
|
|
7
|
+
verify: "VERIFY.md",
|
|
8
|
+
ship: "SHIP.md",
|
|
9
|
+
};
|
|
10
|
+
export function isKdPhase(value) {
|
|
11
|
+
return PHASE_ORDER.includes(value);
|
|
12
|
+
}
|
|
13
|
+
export function nextPhase(phase) {
|
|
14
|
+
const index = PHASE_ORDER.indexOf(phase);
|
|
15
|
+
return index >= 0 ? PHASE_ORDER[index + 1] : undefined;
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type KdProduct = "unknown" | "flagship" | "xinghan" | "cangqiong" | "enterprise";
|
|
2
|
+
export type KdPlatform = "unknown" | "cosmic" | "enterprise-csharp" | "enterprise-python";
|
|
3
|
+
export type KdTechStack = "unknown" | "java-bos" | "java-cosmic" | "csharp-bos" | "python-bos" | "ksql";
|
|
4
|
+
export type KdLanguage = "unknown" | "java" | "csharp" | "python" | "sql";
|
|
5
|
+
export type KnowledgeScope = "common" | "flagship" | "enterprise" | "enterprise-python" | "cosmic" | "xinghan" | "cangqiong";
|
|
6
|
+
export interface ProductProfile {
|
|
7
|
+
product: KdProduct;
|
|
8
|
+
displayName: string;
|
|
9
|
+
platform: KdPlatform;
|
|
10
|
+
techStack: KdTechStack;
|
|
11
|
+
language: KdLanguage;
|
|
12
|
+
knowledgeScope: KnowledgeScope;
|
|
13
|
+
requiresMetadataVerification: boolean;
|
|
14
|
+
notes: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function profileForProduct(product: KdProduct | undefined): ProductProfile;
|
|
17
|
+
export declare function resolveProductProfile(input: string | undefined): ProductProfile;
|
|
18
|
+
export declare function normalizeProduct(value: string | undefined): KdProduct;
|
|
19
|
+
export declare function isKnownProduct(product: KdProduct | undefined): boolean;
|
|
20
|
+
export declare function formatProductProfile(profile: ProductProfile | undefined): string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const PROFILES = {
|
|
2
|
+
unknown: {
|
|
3
|
+
product: "unknown",
|
|
4
|
+
displayName: "unknown",
|
|
5
|
+
platform: "unknown",
|
|
6
|
+
techStack: "unknown",
|
|
7
|
+
language: "unknown",
|
|
8
|
+
knowledgeScope: "common",
|
|
9
|
+
requiresMetadataVerification: true,
|
|
10
|
+
notes: ["尚未选择产品;禁止假设插件技术栈、BOS/Cosmic 平台规则或 KSQL/SQL 交付规则。"],
|
|
11
|
+
},
|
|
12
|
+
flagship: {
|
|
13
|
+
product: "flagship",
|
|
14
|
+
displayName: "金蝶星空旗舰版",
|
|
15
|
+
platform: "cosmic",
|
|
16
|
+
techStack: "java-cosmic",
|
|
17
|
+
language: "java",
|
|
18
|
+
knowledgeScope: "flagship",
|
|
19
|
+
requiresMetadataVerification: true,
|
|
20
|
+
notes: [
|
|
21
|
+
"星空旗舰版基于苍穹/Cosmic 平台。使用平台元数据、插件生命周期、SDK 和后置检查约束,但接口可能存在旗舰版差异。",
|
|
22
|
+
"当前工作区存在 code/ 目录时,产品代码必须放在 code/ 下。",
|
|
23
|
+
"创建或编辑代码前,必须检查 code/ 下真实项目结构,并跟随其实际布局;可能按云、按应用组织,也可能不分模块。",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
xinghan: {
|
|
27
|
+
product: "xinghan",
|
|
28
|
+
displayName: "金蝶星瀚",
|
|
29
|
+
platform: "cosmic",
|
|
30
|
+
techStack: "java-cosmic",
|
|
31
|
+
language: "java",
|
|
32
|
+
knowledgeScope: "xinghan",
|
|
33
|
+
requiresMetadataVerification: true,
|
|
34
|
+
notes: ["星瀚基于苍穹/Cosmic 平台。默认按平台 Java 插件处理,但接口可能存在星瀚差异。"],
|
|
35
|
+
},
|
|
36
|
+
cangqiong: {
|
|
37
|
+
product: "cangqiong",
|
|
38
|
+
displayName: "金蝶苍穹",
|
|
39
|
+
platform: "cosmic",
|
|
40
|
+
techStack: "java-cosmic",
|
|
41
|
+
language: "java",
|
|
42
|
+
knowledgeScope: "cangqiong",
|
|
43
|
+
requiresMetadataVerification: true,
|
|
44
|
+
notes: ["Cosmic 在 KCode 中按苍穹平台语境处理。使用苍穹/Cosmic 平台插件、SDK、元数据、KSQL/SQL 和生命周期规则。"],
|
|
45
|
+
},
|
|
46
|
+
enterprise: {
|
|
47
|
+
product: "enterprise",
|
|
48
|
+
displayName: "金蝶企业版",
|
|
49
|
+
platform: "enterprise-csharp",
|
|
50
|
+
techStack: "csharp-bos",
|
|
51
|
+
language: "csharp",
|
|
52
|
+
knowledgeScope: "enterprise",
|
|
53
|
+
requiresMetadataVerification: true,
|
|
54
|
+
notes: ["使用企业版 C# 技术栈。Java 插件规则和 Cosmic API 不适用。"],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
const PRODUCT_ALIASES = [
|
|
58
|
+
[/企业版|enterprise|csharp|c#|\.net/i, "enterprise"],
|
|
59
|
+
[/星瀚|xinghan/i, "xinghan"],
|
|
60
|
+
[/苍穹|cangqiong/i, "cangqiong"],
|
|
61
|
+
[/cosmic|云苍穹/i, "cangqiong"],
|
|
62
|
+
[/星空旗舰版|星空旗舰|旗舰版|旗舰|flagship/i, "flagship"],
|
|
63
|
+
];
|
|
64
|
+
const ENTERPRISE_PYTHON_PATTERN = /(?:python|py)\s*(?:插件|plugin)|(?:python|py).*?(?:插件|plugin)|ironpython|(?:企业版|enterprise|星空|金蝶云星空|BOS).*?python\s*脚本|python\s*脚本.*?(?:企业版|enterprise|星空|金蝶云星空|BOS)/i;
|
|
65
|
+
function enterprisePythonProfile() {
|
|
66
|
+
return {
|
|
67
|
+
...PROFILES.enterprise,
|
|
68
|
+
platform: "enterprise-python",
|
|
69
|
+
techStack: "python-bos",
|
|
70
|
+
language: "python",
|
|
71
|
+
knowledgeScope: "enterprise-python",
|
|
72
|
+
notes: [
|
|
73
|
+
"只有用户明确要求 Python 插件或 IronPython 脚本时,才使用金蝶云企业版 / BOS IronPython 插件模式。",
|
|
74
|
+
"企业版默认插件开发仍按 C# 处理,除非用户明确要求 Python 插件。",
|
|
75
|
+
"Python 插件通过 BOS 中的 IronPython 运行,并调用 Kingdee.BOS .NET 程序集;编码前必须确认插件类型、事件、FormId、字段标识和注册步骤。",
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function profileForProduct(product) {
|
|
80
|
+
return PROFILES[product ?? "unknown"] ?? PROFILES.unknown;
|
|
81
|
+
}
|
|
82
|
+
export function resolveProductProfile(input) {
|
|
83
|
+
const text = input?.trim();
|
|
84
|
+
if (!text)
|
|
85
|
+
return profileForProduct("unknown");
|
|
86
|
+
if (ENTERPRISE_PYTHON_PATTERN.test(text))
|
|
87
|
+
return enterprisePythonProfile();
|
|
88
|
+
for (const [pattern, product] of PRODUCT_ALIASES) {
|
|
89
|
+
if (pattern.test(text))
|
|
90
|
+
return profileForProduct(product);
|
|
91
|
+
}
|
|
92
|
+
return profileForProduct("unknown");
|
|
93
|
+
}
|
|
94
|
+
export function normalizeProduct(value) {
|
|
95
|
+
return resolveProductProfile(value).product;
|
|
96
|
+
}
|
|
97
|
+
export function isKnownProduct(product) {
|
|
98
|
+
return Boolean(product && product !== "unknown");
|
|
99
|
+
}
|
|
100
|
+
export function formatProductProfile(profile) {
|
|
101
|
+
const resolved = profile ?? profileForProduct("unknown");
|
|
102
|
+
return `${resolved.product}/${resolved.platform}/${resolved.techStack}/${resolved.language}`;
|
|
103
|
+
}
|