kcode-pi 0.1.31 → 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.
@@ -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 中注册监听器,可能导致监听生命周期错误;请移到 registerListener", "p0-initialize-listener"));
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 控件,控件状态可能不生效;请移到 afterBindData 等合适阶段", "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:数据绑定阶段修改模型数据,可能破坏绑定流程;默认值应放到 afterCreateNewData 等阶段", "p0-binddata-setvalue"));
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 的参数不应调用 getOperationResult,请确认事件参数类型", "p0-after-operation-result"));
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:操作事务钩子中独立保存会破坏事务一致性;应直接修改平台传入的数据实体", "p0-transaction-save"));
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;先取对象并判空或用安全取值工具", "p1-dynamicobject-chain-null-risk"));
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,参与运算或解包前应判空或给默认值", "p1-dataset-row-null-risk"));
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 注入风险;请改用参数化查询或 QFilter", "p0-raw-statement"));
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 字符串拼接存在注入风险;请改用参数化查询或 QFilter", "p0-sql-concat"));
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 风险,请禁用外部实体", "p0-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:疑似敏感信息硬编码,请改为安全配置或密文存储", "p0-secret-hardcode"));
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 原生线程会绕过平台线程管理;请使用苍穹 ThreadPools", "p0-native-thread"));
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:循环内刷新视图会造成界面卡顿;应在循环外统一刷新", "p1-loop-update-view"));
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 会造成性能损耗;应在循环外缓存", "p1-loop-field-index"));
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:不要序列化页面、模型或事件大对象做日志,容易造成 CPU 和内存压力;只记录关键字段", "p1-heavy-json-serialization"));
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;分录字段应使用带 rowIndex 的重载", "p1-entry-field-without-row-index"));
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,脏标记会被重新置回;应放到所有 setValue 之后", "p1-set-data-changed-before-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:先 close returnDataToParent 会导致返回数据不执行;应先 returnDataToParent close", "p0-close-before-return-data"));
283
+ results.push(makeResult(i, line, "close", "lifecycle", "error", "P0close 早于 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:生产代码不应使用 System.out.println,请使用平台日志", "p1-system-out"));
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:不要直接 printStackTrace,请使用 logger.error 并传入异常对象", "p2-print-stack-trace"));
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}" 应使用 PascalCase 命名`,
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}" 应使用 camelCase 命名`,
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}" 应使用 UPPER_SNAKE_CASE 命名`,
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
- `提示:${infos.length}`,
542
+ `信息:${infos.length}`,
543
543
  "",
544
544
  ];
545
545
 
@@ -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 前请先设置 product。");
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 插件通常没有本地构建步骤。请通过 BOS 注册和功能测试验证,并在 VERIFY.md 中记录脚本路径、插件类型、FormId、字段/实体标识和测试数据。",
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 需查证 SDK 签名;企业版 C# 需查证命名空间和引用。",
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 需要提供 text 或 path。");
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 需要提供 query 或 className。method 只是在已匹配类/类型内部过滤。",
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
- "只有退出码为 0 时,才能把该结果作为本地 SDK 证据。失败时,应先使用构建输出或项目 SDK 配置查证,再相信随包知识库。",
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 文件。请先构建/复制依赖,或传入 path=<sdk/lib 目录>。",
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 文件。请先构建/还原引用,或传入 path=<sdk/bin 目录>。",
139
+ stderr: "当前项目未找到 dll 文件。执行构建/还原引用,或传入 path=<sdk/bin 目录>。",
140
140
  sources: [],
141
141
  };
142
142
  }