kcode-pi 0.1.34 → 0.1.35
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 +3 -3
- package/docs/CHANGELOG.md +28 -2
- package/docs/COMMAND_REFERENCE.md +10 -10
- package/docs/DEVELOPMENT.md +3 -3
- package/docs/EVIDENCE_AND_GATES.md +8 -8
- package/docs/HARNESS_WORKFLOW.md +12 -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 +52 -49
- 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 +3 -3
- package/src/harness/artifacts.ts +6 -7
- package/src/harness/data-source-policy.ts +302 -0
- package/src/harness/delegation.ts +23 -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 +196 -0
- package/src/harness/prompt.ts +8 -16
- package/src/harness/repair.ts +2 -2
- package/src/harness/state.ts +1 -1
- 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/src/harness/repair.ts
CHANGED
|
@@ -99,7 +99,7 @@ function recordVerifyFailure(cwd: string, run: ActiveRun, input: NormalizedVerif
|
|
|
99
99
|
{
|
|
100
100
|
id: `Q-${String((run.questions?.length ?? 0) + 1).padStart(3, "0")}`,
|
|
101
101
|
phase: "verify",
|
|
102
|
-
question: `验证失败已达到 ${maxAttempts}
|
|
102
|
+
question: `验证失败已达到 ${maxAttempts} 轮。选择继续修复、回到 plan 调整范围或停止。`,
|
|
103
103
|
reason: `最近失败证据:${evidence}`,
|
|
104
104
|
choices: ["继续修复", "回到 plan", "停止"],
|
|
105
105
|
blocking: true,
|
|
@@ -161,7 +161,7 @@ function appendExecuteRepairRecord(cwd: string, run: ActiveRun, evidence: string
|
|
|
161
161
|
"",
|
|
162
162
|
`- 修复轮次:${attempts}/${maxAttempts}`,
|
|
163
163
|
`- 失败证据:${evidence}`,
|
|
164
|
-
"- 下一步:读取失败证据,分析失败原因,只在 PLAN.md
|
|
164
|
+
"- 下一步:读取失败证据,分析失败原因,只在 PLAN.md 批准文件内修复;涉及未批准文件时回到 plan 更新计划。",
|
|
165
165
|
"",
|
|
166
166
|
].join("\n");
|
|
167
167
|
writeArtifact(cwd, run, "execute", `${existing.trimEnd()}\n${section}`);
|
package/src/harness/state.ts
CHANGED
|
@@ -83,7 +83,7 @@ export function createActiveRun(cwd: string, goal: string, productInput?: string
|
|
|
83
83
|
questions: [],
|
|
84
84
|
gate: {
|
|
85
85
|
passed: false,
|
|
86
|
-
reason: "进入 spec
|
|
86
|
+
reason: "进入 spec 前必须完成 CONTEXT.md 并确认产品画像",
|
|
87
87
|
checkedAt: new Date().toISOString(),
|
|
88
88
|
},
|
|
89
89
|
};
|
|
@@ -379,7 +379,7 @@ async function fetchMetadata(config: Record<string, unknown>, target: string): P
|
|
|
379
379
|
const route = objectValue(config.route);
|
|
380
380
|
const routeUrl = routeUrlFromConfig(route);
|
|
381
381
|
if (!routeUrl) {
|
|
382
|
-
throw new Error("未配置表单元数据查询 API
|
|
382
|
+
throw new Error("未配置表单元数据查询 API。必须在 ok-cosmic.json 的 route.apiUrl 中配置统一路由,或设置 COSMIC_ROUTE_API。");
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
const hasCjk = /[\u3400-\u9fff]/.test(target);
|
|
@@ -524,7 +524,7 @@ function runCosmicApi(cwd: string, params: { mode: "search" | "search-method" |
|
|
|
524
524
|
const results = searchKnowledge(query, { scopes: ["cosmic", "cangqiong", "xinghan", "flagship"], topK: params.compact ? 5 : 10, minScore: 1 }, knowledgePath);
|
|
525
525
|
const header = [
|
|
526
526
|
`KCode Node Cosmic API query (${params.mode})`,
|
|
527
|
-
"说明: 当前 npm 包不再调用 Python/SQLite
|
|
527
|
+
"说明: 当前 npm 包不再调用 Python/SQLite 脚本;这里查询随包金蝶知识库。精确方法签名必须结合项目 SDK/编译输出做红绿验证。",
|
|
528
528
|
"",
|
|
529
529
|
].join("\n");
|
|
530
530
|
const stdout = `${header}${formatSearchResults(query, results, knowledgePath)}\n`;
|
|
@@ -690,13 +690,13 @@ function lintStatement(stmt: KsqlStatement): KsqlFinding[] {
|
|
|
690
690
|
findings.push({ severity: "WARN", line: lineOfOffset(stmt.text, stmt.line, match.index ?? 0), message: "SQL 可读性偏好:成员关系/半连接默认使用 IN,只有 IN 改变语义时才保留 EXISTS 并说明原因。" });
|
|
691
691
|
}
|
|
692
692
|
if (/\bUPDATE\b.+\bJOIN\b/i.test(compact)) {
|
|
693
|
-
findings.push({ severity: "WARN", line: stmt.line, message: "PostgreSQL
|
|
693
|
+
findings.push({ severity: "WARN", line: stmt.line, message: "PostgreSQL 多表更新默认使用 UPDATE ... FROM ... WHERE ...,禁止使用 MySQL 风格 UPDATE ... JOIN。" });
|
|
694
694
|
}
|
|
695
695
|
if (/=\s*NULL\b|\bNULL\s*=/i.test(stmt.text)) {
|
|
696
696
|
findings.push({ severity: "ERROR", line: stmt.line, message: "NULL 判断必须使用 IS NULL / IS NOT NULL,不能使用 = NULL。" });
|
|
697
697
|
}
|
|
698
698
|
if (/<>|!=/.test(stmt.text) && hasToken(stmt.text, "NULL")) {
|
|
699
|
-
findings.push({ severity: "WARN", line: stmt.line, message: "涉及 NULL
|
|
699
|
+
findings.push({ severity: "WARN", line: stmt.line, message: "涉及 NULL 的不等比较必须确认语义;PostgreSQL 默认使用 IS DISTINCT FROM。" });
|
|
700
700
|
}
|
|
701
701
|
return findings;
|
|
702
702
|
}
|
package/src/product/profile.ts
CHANGED
|
@@ -24,7 +24,7 @@ const PROFILES: Record<KdProduct, ProductProfile> = {
|
|
|
24
24
|
language: "unknown",
|
|
25
25
|
knowledgeScope: "common",
|
|
26
26
|
requiresMetadataVerification: true,
|
|
27
|
-
notes: ["
|
|
27
|
+
notes: ["尚未选择产品;禁止假设插件技术栈、BOS/Cosmic 平台规则或 KSQL/SQL 交付规则。"],
|
|
28
28
|
},
|
|
29
29
|
flagship: {
|
|
30
30
|
product: "flagship",
|
|
@@ -36,7 +36,7 @@ const PROFILES: Record<KdProduct, ProductProfile> = {
|
|
|
36
36
|
requiresMetadataVerification: true,
|
|
37
37
|
notes: [
|
|
38
38
|
"星空旗舰版基于苍穹/Cosmic 平台。使用平台元数据、插件生命周期、SDK 和后置检查约束,但接口可能存在旗舰版差异。",
|
|
39
|
-
"
|
|
39
|
+
"当前工作区存在 code/ 目录时,产品代码必须放在 code/ 下。",
|
|
40
40
|
"创建或编辑代码前,必须检查 code/ 下真实项目结构,并跟随其实际布局;可能按云、按应用组织,也可能不分模块。",
|
|
41
41
|
],
|
|
42
42
|
},
|
package/src/rules/checker.ts
CHANGED
|
@@ -80,10 +80,10 @@ function checkLifecycleMisuse(lines: string[]): CheckResult[] {
|
|
|
80
80
|
for (const range of initializeRanges) {
|
|
81
81
|
forEachLineInRange(lines, range, (line, index) => {
|
|
82
82
|
if (/addItemClickListener\s*\(/.test(line)) {
|
|
83
|
-
results.push(makeResult(index, line, "addItemClickListener", "lifecycle", "error", "P0:initialize
|
|
83
|
+
results.push(makeResult(index, line, "addItemClickListener", "lifecycle", "error", "P0:initialize 中注册监听器,可能导致监听生命周期错误;必须移到 registerListener", "p0-initialize-listener"));
|
|
84
84
|
}
|
|
85
85
|
if (/getView\s*\(\)\s*\.\s*(setVisible|setEnable|updateView|showConfirm|showTipNotification)\s*\(/.test(line)) {
|
|
86
|
-
results.push(makeResult(index, line, "getView", "lifecycle", "error", "P0:initialize 中操作 UI
|
|
86
|
+
results.push(makeResult(index, line, "getView", "lifecycle", "error", "P0:initialize 中操作 UI 控件,控件状态可能不生效;必须移到 afterBindData 等合适阶段", "p0-initialize-ui"));
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
}
|
|
@@ -91,7 +91,7 @@ function checkLifecycleMisuse(lines: string[]): CheckResult[] {
|
|
|
91
91
|
for (const range of [...beforeBindRanges, ...afterBindRanges]) {
|
|
92
92
|
forEachLineInRange(lines, range, (line, index) => {
|
|
93
93
|
if (/(getModel\s*\(\)\s*\.\s*)?setValue\s*\(/.test(line)) {
|
|
94
|
-
results.push(makeResult(index, line, "setValue", "lifecycle", "error", "P0
|
|
94
|
+
results.push(makeResult(index, line, "setValue", "lifecycle", "error", "P0:数据绑定阶段修改模型数据,可能破坏绑定流程;默认值必须放到 afterCreateNewData 等阶段", "p0-binddata-setvalue"));
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
}
|
|
@@ -99,7 +99,7 @@ function checkLifecycleMisuse(lines: string[]): CheckResult[] {
|
|
|
99
99
|
for (const range of afterOperationRanges) {
|
|
100
100
|
forEachLineInRange(lines, range, (line, index) => {
|
|
101
101
|
if (/getOperationResult\s*\(/.test(line)) {
|
|
102
|
-
results.push(makeResult(index, line, "getOperationResult", "lifecycle", "error", "P0:afterExecuteOperationTransaction
|
|
102
|
+
results.push(makeResult(index, line, "getOperationResult", "lifecycle", "error", "P0:afterExecuteOperationTransaction 的参数禁止调用 getOperationResult;必须确认事件参数类型", "p0-after-operation-result"));
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
105
|
}
|
|
@@ -114,7 +114,7 @@ function checkTransactionMisuse(lines: string[]): CheckResult[] {
|
|
|
114
114
|
for (const range of ranges) {
|
|
115
115
|
forEachLineInRange(lines, range, (line, index) => {
|
|
116
116
|
if (/SaveServiceHelper\s*\.\s*(save|update)\s*\(/.test(line)) {
|
|
117
|
-
results.push(makeResult(index, line, "SaveServiceHelper", "transaction", "error", "P0
|
|
117
|
+
results.push(makeResult(index, line, "SaveServiceHelper", "transaction", "error", "P0:操作事务钩子中独立保存会破坏事务一致性;必须直接修改平台传入的数据实体", "p0-transaction-save"));
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -139,10 +139,10 @@ function checkResourceHandling(lines: string[]): CheckResult[] {
|
|
|
139
139
|
const line = lines[i];
|
|
140
140
|
if (isCommentLine(line)) continue;
|
|
141
141
|
if (/\.\s*getDynamicObject\s*\([^)]*\)\s*\.\s*get(?:String|Long|Int|Integer|Double|Date|PkValue|DynamicObject|DynamicObjectCollection)\s*\(/.test(line)) {
|
|
142
|
-
results.push(makeResult(i, line, "getDynamicObject", "resource", "warning", "P1:嵌套 DynamicObject 直接取值缺少空值保护,引用字段为空时可能 NPE
|
|
142
|
+
results.push(makeResult(i, line, "getDynamicObject", "resource", "warning", "P1:嵌套 DynamicObject 直接取值缺少空值保护,引用字段为空时可能 NPE;取对象并判空后再取值,或使用安全取值工具", "p1-dynamicobject-chain-null-risk"));
|
|
143
143
|
}
|
|
144
144
|
if (/row\s*\.\s*get(?:BigDecimal|String|Long|Int|Integer|Date)\s*\(/.test(line) && !nearbyLineHas(lines, i, -2, /\brow\b\s*(?:!=|==)\s*null|Optional\.ofNullable\s*\(\s*row\s*\)|Objects\.nonNull\s*\(\s*row\s*\)/)) {
|
|
145
|
-
results.push(makeResult(i, line, "row", "resource", "warning", "P1:DataSet.Row 取值可能返回 null
|
|
145
|
+
results.push(makeResult(i, line, "row", "resource", "warning", "P1:DataSet.Row 取值可能返回 null,参与运算或解包前必须判空或给默认值", "p1-dataset-row-null-risk"));
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -157,19 +157,19 @@ function checkSecurityPatterns(lines: string[]): CheckResult[] {
|
|
|
157
157
|
if (isCommentLine(line)) continue;
|
|
158
158
|
|
|
159
159
|
if (/\bStatement\b/.test(line) && !/\bPreparedStatement\b/.test(line)) {
|
|
160
|
-
results.push(makeResult(i, line, "Statement", "security", "error", "P0:使用 Statement 存在 SQL
|
|
160
|
+
results.push(makeResult(i, line, "Statement", "security", "error", "P0:使用 Statement 存在 SQL 注入风险;必须改用参数化查询或 QFilter", "p0-raw-statement"));
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
if (/"\s*(SELECT|UPDATE|DELETE|INSERT)\b[^"]*"\s*\+|\+\s*"\s*(WHERE|AND|OR|SET)\b/i.test(line)) {
|
|
164
|
-
results.push(makeResult(i, line, "+", "security", "error", "P0:SQL
|
|
164
|
+
results.push(makeResult(i, line, "+", "security", "error", "P0:SQL 字符串拼接存在注入风险;必须改用参数化查询或 QFilter", "p0-sql-concat"));
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
if (/<!DOCTYPE|<!ENTITY|\bSYSTEM\b/.test(line)) {
|
|
168
|
-
results.push(makeResult(i, line, "XML", "security", "error", "P0:XML 外部实体相关内容可能导致 XXE
|
|
168
|
+
results.push(makeResult(i, line, "XML", "security", "error", "P0:XML 外部实体相关内容可能导致 XXE 风险;必须禁用外部实体", "p0-xxe"));
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
if (/(password|passwd|secret|token|ak|sk)\s*=\s*"[^"]{4,}"/i.test(line)) {
|
|
172
|
-
results.push(makeResult(i, line, "=", "security", "error", "P0
|
|
172
|
+
results.push(makeResult(i, line, "=", "security", "error", "P0:疑似敏感信息硬编码;必须改为安全配置或密文存储", "p0-secret-hardcode"));
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
if (/\bcache\s*\.\s*put\s*\(/i.test(line) || /\bCacheFactory\b/.test(line)) {
|
|
@@ -197,7 +197,7 @@ function checkThreadingPatterns(lines: string[]): CheckResult[] {
|
|
|
197
197
|
const line = lines[i];
|
|
198
198
|
if (isCommentLine(line)) continue;
|
|
199
199
|
if (/new\s+Thread\s*\(|Executors\s*\.\s*new\w+/.test(line)) {
|
|
200
|
-
results.push(makeResult(i, line, "Thread", "threading", "error", "P0:使用 JDK
|
|
200
|
+
results.push(makeResult(i, line, "Thread", "threading", "error", "P0:使用 JDK 原生线程会绕过平台线程管理;必须使用苍穹 ThreadPools", "p0-native-thread"));
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
@@ -217,15 +217,15 @@ function checkUiPerformance(lines: string[]): CheckResult[] {
|
|
|
217
217
|
if (/endInit\s*\(/.test(line)) endInitCount++;
|
|
218
218
|
|
|
219
219
|
if (isInsideLoop(lines, i) && /updateView\s*\(/.test(line)) {
|
|
220
|
-
results.push(makeResult(i, line, "updateView", "performance", "warning", "P1
|
|
220
|
+
results.push(makeResult(i, line, "updateView", "performance", "warning", "P1:循环内刷新视图会造成界面卡顿;必须在循环外统一刷新", "p1-loop-update-view"));
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
if (isInsideLoop(lines, i) && /getFieldIndex\s*\(/.test(line)) {
|
|
224
|
-
results.push(makeResult(i, line, "getFieldIndex", "performance", "warning", "P1:循环内重复获取 FieldIndex
|
|
224
|
+
results.push(makeResult(i, line, "getFieldIndex", "performance", "warning", "P1:循环内重复获取 FieldIndex 会造成性能损耗;必须在循环外缓存", "p1-loop-field-index"));
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
if (/SerializationUtils\s*\.\s*toJsonString\s*\(/.test(line) && /(view|model|event|page|args|this|getView\s*\(|getModel\s*\(|\be\b)/i.test(line)) {
|
|
228
|
-
results.push(makeResult(i, line, "SerializationUtils", "performance", "warning", "P1
|
|
228
|
+
results.push(makeResult(i, line, "SerializationUtils", "performance", "warning", "P1:禁止序列化页面、模型或事件大对象做日志,容易造成 CPU 和内存压力;只记录关键字段", "p1-heavy-json-serialization"));
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
const entryUiMatch = line.match(/\bgetView\s*\(\)\s*\.\s*(setEnable|setVisible)\s*\(/);
|
|
@@ -235,7 +235,7 @@ function checkUiPerformance(lines: string[]): CheckResult[] {
|
|
|
235
235
|
if (args.length >= 2 && !looksLikeRowIndexedViewCall(args)) {
|
|
236
236
|
const fieldArgs = args.slice(1).join(",");
|
|
237
237
|
if (/(entry|entries|detail|row|qty|price|amount|material|item)/i.test(fieldArgs)) {
|
|
238
|
-
results.push(makeResult(i, line, entryUiMatch[1], "performance", "warning", "P1:疑似对分录字段使用单头 setEnable/setVisible
|
|
238
|
+
results.push(makeResult(i, line, entryUiMatch[1], "performance", "warning", "P1:疑似对分录字段使用单头 setEnable/setVisible;分录字段必须使用带 rowIndex 的重载", "p1-entry-field-without-row-index"));
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
}
|
|
@@ -262,7 +262,7 @@ function checkViewInteractionOrder(lines: string[]): CheckResult[] {
|
|
|
262
262
|
for (let j = i + 1; j < Math.min(lines.length, i + 25); j++) {
|
|
263
263
|
if (/^\s*}\s*$/.test(lines[j])) break;
|
|
264
264
|
if (!isCommentLine(lines[j]) && /(getModel\s*\(\)\s*\.\s*)?setValue\s*\(/.test(lines[j])) {
|
|
265
|
-
results.push(makeResult(i, line, "setDataChanged", "lifecycle", "warning", "P1:setDataChanged(false) 后仍继续 setValue
|
|
265
|
+
results.push(makeResult(i, line, "setDataChanged", "lifecycle", "warning", "P1:setDataChanged(false) 后仍继续 setValue,脏标记会被重新置回;必须放到所有 setValue 之后", "p1-set-data-changed-before-setvalue"));
|
|
266
266
|
break;
|
|
267
267
|
}
|
|
268
268
|
}
|
|
@@ -280,7 +280,7 @@ function checkViewInteractionOrder(lines: string[]): CheckResult[] {
|
|
|
280
280
|
for (let j = i + 1; j < Math.min(lines.length, i + 10); j++) {
|
|
281
281
|
if (/^\s*}\s*$/.test(lines[j])) break;
|
|
282
282
|
if (!isCommentLine(lines[j]) && /returnDataToParent\s*\(/.test(lines[j])) {
|
|
283
|
-
results.push(makeResult(i, line, "close", "lifecycle", "error", "P0
|
|
283
|
+
results.push(makeResult(i, line, "close", "lifecycle", "error", "P0:close 早于 returnDataToParent 会导致返回数据不执行;必须执行 returnDataToParent 后再 close", "p0-close-before-return-data"));
|
|
284
284
|
break;
|
|
285
285
|
}
|
|
286
286
|
}
|
|
@@ -298,11 +298,11 @@ function checkLoggingAndDiagnostics(lines: string[]): CheckResult[] {
|
|
|
298
298
|
if (isCommentLine(line)) continue;
|
|
299
299
|
|
|
300
300
|
if (/System\s*\.\s*out\s*\.\s*println\s*\(/.test(line)) {
|
|
301
|
-
results.push(makeResult(i, line, "System.out.println", "exception", "warning", "P1
|
|
301
|
+
results.push(makeResult(i, line, "System.out.println", "exception", "warning", "P1:生产代码禁止使用 System.out.println;必须使用平台日志", "p1-system-out"));
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
if (/\.printStackTrace\s*\(/.test(line)) {
|
|
305
|
-
results.push(makeResult(i, line, "printStackTrace", "exception", "warning", "P2
|
|
305
|
+
results.push(makeResult(i, line, "printStackTrace", "exception", "warning", "P2:禁止直接 printStackTrace;必须使用 logger.error 并传入异常对象", "p2-print-stack-trace"));
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
@@ -326,7 +326,7 @@ function checkMagicValues(lines: string[]): CheckResult[] {
|
|
|
326
326
|
column: match.index ?? 0,
|
|
327
327
|
type: "magic_value",
|
|
328
328
|
severity: "warning",
|
|
329
|
-
message: `检测到硬编码数字 ${value}
|
|
329
|
+
message: `检测到硬编码数字 ${value},必须改为常量或配置`,
|
|
330
330
|
rule: "magic-number",
|
|
331
331
|
});
|
|
332
332
|
}
|
|
@@ -350,7 +350,7 @@ function checkMagicValues(lines: string[]): CheckResult[] {
|
|
|
350
350
|
column: match.index ?? 0,
|
|
351
351
|
type: "magic_value",
|
|
352
352
|
severity: "warning",
|
|
353
|
-
message: `检测到可能的业务常量 "${value}"
|
|
353
|
+
message: `检测到可能的业务常量 "${value}",必须定义为常量`,
|
|
354
354
|
rule: "magic-string",
|
|
355
355
|
});
|
|
356
356
|
}
|
|
@@ -378,7 +378,7 @@ function checkNaming(lines: string[], language: CheckLanguage): CheckResult[] {
|
|
|
378
378
|
column: line.indexOf(className),
|
|
379
379
|
type: "naming",
|
|
380
380
|
severity: "error",
|
|
381
|
-
message: `类名 "${className}"
|
|
381
|
+
message: `类名 "${className}" 必须使用 PascalCase 命名`,
|
|
382
382
|
rule: "class-naming",
|
|
383
383
|
});
|
|
384
384
|
}
|
|
@@ -401,7 +401,7 @@ function checkNaming(lines: string[], language: CheckLanguage): CheckResult[] {
|
|
|
401
401
|
column: line.indexOf(methodName),
|
|
402
402
|
type: "naming",
|
|
403
403
|
severity: "warning",
|
|
404
|
-
message: `方法名 "${methodName}"
|
|
404
|
+
message: `方法名 "${methodName}" 必须使用 camelCase 命名`,
|
|
405
405
|
rule: "method-naming",
|
|
406
406
|
});
|
|
407
407
|
}
|
|
@@ -416,7 +416,7 @@ function checkNaming(lines: string[], language: CheckLanguage): CheckResult[] {
|
|
|
416
416
|
column: line.indexOf(constantName),
|
|
417
417
|
type: "naming",
|
|
418
418
|
severity: "warning",
|
|
419
|
-
message: `常量 "${constantName}"
|
|
419
|
+
message: `常量 "${constantName}" 必须使用 UPPER_SNAKE_CASE 命名`,
|
|
420
420
|
rule: "constant-naming",
|
|
421
421
|
});
|
|
422
422
|
}
|
|
@@ -519,7 +519,7 @@ function checkExceptionHandling(lines: string[]): CheckResult[] {
|
|
|
519
519
|
column: line.indexOf("catch"),
|
|
520
520
|
type: "exception",
|
|
521
521
|
severity: "error",
|
|
522
|
-
message: "空的 catch
|
|
522
|
+
message: "空的 catch 块,必须记录日志或处理异常",
|
|
523
523
|
rule: "empty-catch",
|
|
524
524
|
});
|
|
525
525
|
}
|
|
@@ -539,7 +539,7 @@ export function formatCheckResults(results: CheckResult[]): string {
|
|
|
539
539
|
`金蝶代码检查发现 ${results.length} 个问题:`,
|
|
540
540
|
`错误:${errors.length}`,
|
|
541
541
|
`警告:${warnings.length}`,
|
|
542
|
-
|
|
542
|
+
`信息:${infos.length}`,
|
|
543
543
|
"",
|
|
544
544
|
];
|
|
545
545
|
|
package/src/tools/build-debug.ts
CHANGED
|
@@ -20,14 +20,14 @@ export interface DebugFinding {
|
|
|
20
20
|
|
|
21
21
|
export function planBuild(cwd: string, profile: ProductProfile, target?: string): BuildPlan {
|
|
22
22
|
if (profile.product === "unknown") {
|
|
23
|
-
throw new Error("产品画像未知。运行 kd_build
|
|
23
|
+
throw new Error("产品画像未知。运行 kd_build 前必须设置 product。");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (profile.platform === "cosmic") return planJavaBuild(cwd, profile, target);
|
|
27
27
|
if (profile.platform === "enterprise-csharp") return planCsharpBuild(cwd, profile, target);
|
|
28
28
|
if (profile.platform === "enterprise-python") {
|
|
29
29
|
throw new Error(
|
|
30
|
-
"企业版 Python
|
|
30
|
+
"企业版 Python 插件通常没有本地构建步骤。LLM 不能替代 BOS 注册和功能测试;必须要求用户提供验证结果,并在 VERIFY.md 记录脚本路径、插件类型、FormId、字段/实体标识、测试数据和用户侧验证证据。",
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
throw new Error(`未找到 ${profile.product}/${profile.platform}/${profile.techStack} 的构建策略。`);
|
|
@@ -105,7 +105,7 @@ export function analyzeDebugText(text: string): DebugFinding[] {
|
|
|
105
105
|
severity: "error",
|
|
106
106
|
rule: "build-error",
|
|
107
107
|
message: "检测到构建或编译错误。",
|
|
108
|
-
nextStep: "使用产品对应的构建输出。Cosmic Java
|
|
108
|
+
nextStep: "使用产品对应的构建输出。Cosmic Java 必须查证 SDK 签名;企业版 C# 必须查证命名空间和引用。",
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
if (findings.length === 0) {
|
|
@@ -114,7 +114,7 @@ export function analyzeDebugText(text: string): DebugFinding[] {
|
|
|
114
114
|
rule: "no-known-pattern",
|
|
115
115
|
message: "未匹配到已知金蝶调试模式。",
|
|
116
116
|
evidence: "",
|
|
117
|
-
nextStep: "
|
|
117
|
+
nextStep: "补充完整日志、堆栈、产品/版本、目标单据/表单和最近代码改动。",
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -138,7 +138,7 @@ export function formatDebugFindings(findings: DebugFinding[]): string {
|
|
|
138
138
|
|
|
139
139
|
export function readDebugInput(cwd: string, text?: string, path?: string): { source: string; text: string } {
|
|
140
140
|
if (text) return { source: "inline", text };
|
|
141
|
-
if (!path) throw new Error("kd_debug
|
|
141
|
+
if (!path) throw new Error("kd_debug 参数要求:提供 text 或 path。");
|
|
142
142
|
|
|
143
143
|
const fullPath = resolveWorkspacePath(cwd, path);
|
|
144
144
|
return { source: path, text: readFileSync(fullPath, "utf8") };
|
|
@@ -46,7 +46,7 @@ export async function inspectSdkSignature(cwd: string, params: SdkSignatureParam
|
|
|
46
46
|
query,
|
|
47
47
|
exitCode: 2,
|
|
48
48
|
stdout: "",
|
|
49
|
-
stderr: "kd_sdk_signature
|
|
49
|
+
stderr: "kd_sdk_signature 参数要求:提供 query 或 className。method 仅在已匹配类/类型内部过滤。",
|
|
50
50
|
sources: [],
|
|
51
51
|
};
|
|
52
52
|
}
|
|
@@ -64,7 +64,7 @@ export function formatSdkSignatureResult(result: SdkSignatureResult): string {
|
|
|
64
64
|
result.stdout.trim() ? `\nSTDOUT:\n${result.stdout.trim()}` : undefined,
|
|
65
65
|
result.stderr.trim() ? `\nSTDERR:\n${result.stderr.trim()}` : undefined,
|
|
66
66
|
"",
|
|
67
|
-
"
|
|
67
|
+
"证据规则:只有退出码为 0 时,该结果才能作为本地 SDK 证据。失败时必须使用构建输出或项目 SDK 配置查证;禁止用随包知识库替代。",
|
|
68
68
|
]
|
|
69
69
|
.filter(Boolean)
|
|
70
70
|
.join("\n");
|
|
@@ -79,7 +79,7 @@ async function inspectJavaSignature(cwd: string, params: SdkSignatureParams, que
|
|
|
79
79
|
query,
|
|
80
80
|
exitCode: 2,
|
|
81
81
|
stdout: "",
|
|
82
|
-
stderr: "当前项目未找到 jar
|
|
82
|
+
stderr: "当前项目未找到 jar 文件。执行构建/复制依赖,或传入 path=<sdk/lib 目录>。",
|
|
83
83
|
sources: [],
|
|
84
84
|
};
|
|
85
85
|
}
|
|
@@ -136,7 +136,7 @@ async function inspectCsharpSignature(cwd: string, params: SdkSignatureParams, q
|
|
|
136
136
|
query,
|
|
137
137
|
exitCode: 2,
|
|
138
138
|
stdout: "",
|
|
139
|
-
stderr: "当前项目未找到 dll
|
|
139
|
+
stderr: "当前项目未找到 dll 文件。执行构建/还原引用,或传入 path=<sdk/bin 目录>。",
|
|
140
140
|
sources: [],
|
|
141
141
|
};
|
|
142
142
|
}
|