kcode-pi 0.1.14 → 0.1.16

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.
@@ -1,6 +1,6 @@
1
1
  import { dirname, join } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import { readFileSync } from "node:fs";
3
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { Type } from "@earendil-works/pi-ai";
5
5
  import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { formatSearchResults, formatTableSchema } from "../src/knowledge/format.ts";
@@ -22,6 +22,9 @@ import {
22
22
  writeOfficialEvidence,
23
23
  } from "../src/official/kingdee-skills.ts";
24
24
  import { resolveWorkspacePath } from "../src/platform/path.ts";
25
+ import { readActiveRun } from "../src/harness/state.ts";
26
+ import { runArtifactPath } from "../src/harness/paths.ts";
27
+ import { SDK_SIGNATURE_EVIDENCE } from "../src/harness/sdk-policy.ts";
25
28
 
26
29
  const extensionDir = dirname(fileURLToPath(import.meta.url));
27
30
  const knowledgePath = join(extensionDir, "..", "knowledge");
@@ -74,8 +77,8 @@ function sdkLanguageForProfile(profile: ProductProfile, value: string | undefine
74
77
 
75
78
  function rejectNonCosmic(profile: ProductProfile): string | undefined {
76
79
  if (isCosmicFamily(profile)) return undefined;
77
- if (profile.product === "unknown") return "Provide a Cosmic-family product first: cangqiong, xinghan, flagship, or cosmic.";
78
- return `Product ${profile.product} uses ${profile.platform}/${profile.techStack}; Cosmic official scripts are not applicable.`;
80
+ if (profile.product === "unknown") return "请先提供 Cosmic 家族产品:cangqiongxinghanflagship cosmic";
81
+ return `当前产品 ${profile.product} 使用 ${profile.platform}/${profile.techStack},不适用 Cosmic 官方能力。`;
79
82
  }
80
83
 
81
84
  async function runOrDryRun(
@@ -87,7 +90,7 @@ async function runOrDryRun(
87
90
  const command = await commandPromise;
88
91
  if (dryRun) {
89
92
  return {
90
- content: [{ type: "text" as const, text: `Dry run command:\n${command.display}` }],
93
+ content: [{ type: "text" as const, text: `仅展示命令,不执行:\n${command.display}` }],
91
94
  details: { command: command.display, dryRun: true },
92
95
  };
93
96
  }
@@ -102,13 +105,13 @@ async function runOrDryRun(
102
105
 
103
106
  const kdSearchTool = defineTool({
104
107
  name: "kd_search",
105
- label: "KD Search",
106
- description: "Search bundled Kingdee SDK knowledge, plugin lifecycle notes, code patterns, and common implementation guidance.",
108
+ label: "KD 搜索",
109
+ description: "搜索 KCode 随包金蝶知识库,包括 SDK、插件生命周期、代码模式和常见实现建议。",
107
110
  parameters: Type.Object({
108
- query: Type.String({ description: "Keyword or API/class/table/lifecycle term to search for" }),
109
- product: Type.Optional(Type.String({ description: "Kingdee product: flagship, cosmic, xinghan, cangqiong, or enterprise." })),
110
- edition: Type.Optional(Type.String({ description: "Legacy alias for product. Prefer product." })),
111
- limit: Type.Optional(Type.Number({ description: "Maximum number of results. Defaults to 5." })),
111
+ query: Type.String({ description: "要搜索的关键词、API、类名、表名或生命周期术语。" }),
112
+ product: Type.Optional(Type.String({ description: "金蝶产品:flagshipcosmicxinghancangqiong enterprise" })),
113
+ edition: Type.Optional(Type.String({ description: "旧参数,等同于 product。优先使用 product" })),
114
+ limit: Type.Optional(Type.Number({ description: "最大结果数,默认 5" })),
112
115
  }),
113
116
 
114
117
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
@@ -117,15 +120,15 @@ const kdSearchTool = defineTool({
117
120
  if (!scopes) {
118
121
  const guidance =
119
122
  profile.product === "unknown"
120
- ? "Provide product first, for example product=flagship, enterprise, cosmic, xinghan, or cangqiong."
121
- : "No knowledge scope is configured for this product profile.";
123
+ ? "请先提供 product,例如 flagshipenterprisecosmicxinghan cangqiong"
124
+ : "当前产品画像未配置可搜索知识范围。";
122
125
  return {
123
126
  content: [
124
127
  {
125
128
  type: "text",
126
129
  text: [
127
- `Product profile: ${profile.product}/${profile.techStack}/${profile.language}`,
128
- "Bundled searchable knowledge requires a known product profile.",
130
+ `产品画像:${profile.product}/${profile.techStack}/${profile.language}`,
131
+ "KCode 随包知识库搜索需要明确产品画像。",
129
132
  guidance,
130
133
  ].join("\n"),
131
134
  },
@@ -145,12 +148,12 @@ const kdSearchTool = defineTool({
145
148
 
146
149
  const kdTableTool = defineTool({
147
150
  name: "kd_table",
148
- label: "KD Table",
149
- description: "Look up a bundled Kingdee table schema by table name for the selected product profile.",
151
+ label: "KD 表结构",
152
+ description: "按表名查询 KCode 随包金蝶表结构,查询结果受产品画像约束。",
150
153
  parameters: Type.Object({
151
- table: Type.String({ description: "Table name, for example T_PUR_POORDER" }),
152
- product: Type.Optional(Type.String({ description: "Kingdee product: flagship, cosmic, xinghan, cangqiong, or enterprise." })),
153
- edition: Type.Optional(Type.String({ description: "Legacy alias for product. Prefer product." })),
154
+ table: Type.String({ description: "表名,例如 T_PUR_POORDER" }),
155
+ product: Type.Optional(Type.String({ description: "金蝶产品:flagshipcosmicxinghancangqiong enterprise" })),
156
+ edition: Type.Optional(Type.String({ description: "旧参数,等同于 product。优先使用 product" })),
154
157
  }),
155
158
 
156
159
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
@@ -159,15 +162,15 @@ const kdTableTool = defineTool({
159
162
  if (!edition) {
160
163
  const guidance =
161
164
  profile.product === "unknown"
162
- ? "Provide product first. Do not assume a table schema across Kingdee product families."
163
- : "Do not reuse flagship/enterprise table assumptions for Cosmic/Xinghan/Cangqiong without metadata verification.";
165
+ ? "请先提供 product。不要跨金蝶产品族猜测表结构。"
166
+ : "没有元数据查证前,不要把旗舰版/企业版表结构假设复用到苍穹/星瀚/Cosmic";
164
167
  return {
165
168
  content: [
166
169
  {
167
170
  type: "text",
168
171
  text: [
169
- `Product profile: ${profile.product}/${profile.techStack}/${profile.language}`,
170
- "Bundled table schemas currently cover flagship and enterprise only.",
172
+ `产品画像:${profile.product}/${profile.techStack}/${profile.language}`,
173
+ "KCode 随包表结构目前主要覆盖旗舰版和企业版。",
171
174
  guidance,
172
175
  ].join("\n"),
173
176
  },
@@ -186,14 +189,14 @@ const kdTableTool = defineTool({
186
189
 
187
190
  const kdCheckTool = defineTool({
188
191
  name: "kd_check",
189
- label: "KD Check",
192
+ label: "KD 检查",
190
193
  description:
191
- "Check Kingdee Java/C#/Python plugin code for magic values, naming issues, DB calls in loops, and empty catch blocks.",
194
+ "检查金蝶 Java/C#/Python 插件代码中的魔法值、命名问题、循环内 DB 调用和空 catch 等问题。",
192
195
  parameters: Type.Object({
193
- code: Type.Optional(Type.String({ description: "Source code to check. Use this or path." })),
194
- path: Type.Optional(Type.String({ description: "Path to a source file to check. Use this or code." })),
195
- product: Type.Optional(Type.String({ description: "Kingdee product. Used to derive Java or C# when language is omitted." })),
196
- language: Type.Optional(Type.String({ description: "Language: java, csharp, or python. Overrides product-derived language." })),
196
+ code: Type.Optional(Type.String({ description: "要检查的源码。与 path 二选一。" })),
197
+ path: Type.Optional(Type.String({ description: "要检查的源码文件路径。与 code 二选一。" })),
198
+ product: Type.Optional(Type.String({ description: "金蝶产品。未提供 language 时用于推导 Java C#。" })),
199
+ language: Type.Optional(Type.String({ description: "语言:javacsharp python。会覆盖产品推导结果。" })),
197
200
  }),
198
201
 
199
202
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -210,7 +213,7 @@ const kdCheckTool = defineTool({
210
213
 
211
214
  if (!code) {
212
215
  return {
213
- content: [{ type: "text", text: "Provide either code or path for kd_check." }],
216
+ content: [{ type: "text", text: "kd_check 需要提供 code path" }],
214
217
  details: { error: "missing-code-or-path" },
215
218
  };
216
219
  }
@@ -220,7 +223,7 @@ const kdCheckTool = defineTool({
220
223
  content: [
221
224
  {
222
225
  type: "text",
223
- text: `Source: ${source}\nProduct: ${profile.product}/${profile.techStack}\nLanguage: ${language}\n\n${formatCheckResults(results)}`,
226
+ text: `来源:${source}\n产品:${profile.product}/${profile.techStack}\n语言:${language}\n\n${formatCheckResults(results)}`,
224
227
  },
225
228
  ],
226
229
  details: { source, product: profile.product, language, issues: results },
@@ -230,12 +233,12 @@ const kdCheckTool = defineTool({
230
233
 
231
234
  const kdCosmicConfigTool = defineTool({
232
235
  name: "kd_cosmic_config",
233
- label: "KD Cosmic Config",
234
- description: "Run the official ok-cosmic configuration precheck for Cosmic-family Kingdee products.",
236
+ label: "KD Cosmic 配置",
237
+ description: "运行 Cosmic 家族金蝶产品的官方能力配置预检查。",
235
238
  parameters: Type.Object({
236
- product: Type.String({ description: "Cosmic-family product: cangqiong, xinghan, flagship, or cosmic." }),
237
- config: Type.Optional(Type.String({ description: "Optional ok-cosmic.json path. Defaults to current working directory behavior." })),
238
- dryRun: Type.Optional(Type.Boolean({ description: "Return the command without executing it." })),
239
+ product: Type.String({ description: "Cosmic 家族产品:cangqiongxinghanflagship cosmic" }),
240
+ config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。默认按当前工作目录解析。" })),
241
+ dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
239
242
  }),
240
243
 
241
244
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -248,18 +251,18 @@ const kdCosmicConfigTool = defineTool({
248
251
 
249
252
  const kdCosmicMetadataTool = defineTool({
250
253
  name: "kd_cosmic_metadata",
251
- label: "KD Cosmic Metadata",
252
- description: "Query official Cosmic form/bill metadata for fields, enum values, operations, and SQL table details.",
254
+ label: "KD Cosmic 元数据",
255
+ description: "查询官方 Cosmic 表单/单据元数据,包括字段、枚举值、操作和 SQL 表信息。",
253
256
  parameters: Type.Object({
254
- product: Type.String({ description: "Cosmic-family product: cangqiong, xinghan, flagship, or cosmic." }),
255
- form: Type.String({ description: "Form ID, bill ID, or Chinese bill name. Multiple targets can be comma-separated." }),
256
- config: Type.Optional(Type.String({ description: "Optional ok-cosmic.json path." })),
257
- fuzzy: Type.Optional(Type.String({ description: "Optional field keywords separated by spaces or commas." })),
258
- typeFilter: Type.Optional(Type.String({ description: "Optional field type regex, for example combo|check or decimal." })),
259
- sql: Type.Optional(Type.Boolean({ description: "Include database table and field information." })),
260
- op: Type.Optional(Type.Boolean({ description: "Show operations for the form/bill." })),
261
- showDetail: Type.Optional(Type.Boolean({ description: "Show detailed metadata output." })),
262
- dryRun: Type.Optional(Type.Boolean({ description: "Return the command without executing it." })),
257
+ product: Type.String({ description: "Cosmic 家族产品:cangqiongxinghanflagship cosmic" }),
258
+ form: Type.String({ description: "Form ID、单据 ID 或中文单据名;多个目标可用逗号分隔。" }),
259
+ config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。" })),
260
+ fuzzy: Type.Optional(Type.String({ description: "可选字段关键词,用空格或逗号分隔。" })),
261
+ typeFilter: Type.Optional(Type.String({ description: "可选字段类型正则,例如 combo|check decimal" })),
262
+ sql: Type.Optional(Type.Boolean({ description: "是否包含数据库表和字段信息。" })),
263
+ op: Type.Optional(Type.Boolean({ description: "是否显示表单/单据操作。" })),
264
+ showDetail: Type.Optional(Type.Boolean({ description: "是否显示详细元数据输出。" })),
265
+ dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
263
266
  }),
264
267
 
265
268
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -273,15 +276,15 @@ const kdCosmicMetadataTool = defineTool({
273
276
  const kdCosmicApiTool = defineTool({
274
277
  name: "kd_cosmic_api",
275
278
  label: "KD Cosmic API",
276
- description: "Query bundled Cosmic API knowledge for class and method clues. Use kd_sdk_signature or build output for final local SDK facts.",
279
+ description: "查询随包 Cosmic API 知识,获取类和方法线索;最终签名事实应以 kd_sdk_signature 或项目构建输出为准。",
277
280
  parameters: Type.Object({
278
- product: Type.String({ description: "Cosmic-family product: cangqiong, xinghan, flagship, or cosmic." }),
279
- mode: Type.String({ description: "search, search-method, or detail." }),
280
- query: Type.String({ description: "Class name, method name, or fully qualified class name." }),
281
- config: Type.Optional(Type.String({ description: "Optional ok-cosmic.json path." })),
282
- method: Type.Optional(Type.String({ description: "Optional method filter for detail mode." })),
283
- compact: Type.Optional(Type.Boolean({ description: "Request compact detail output when supported." })),
284
- dryRun: Type.Optional(Type.Boolean({ description: "Return the command without executing it." })),
281
+ product: Type.String({ description: "Cosmic 家族产品:cangqiongxinghanflagship cosmic" }),
282
+ mode: Type.String({ description: "查询模式:searchsearch-method detail" }),
283
+ query: Type.String({ description: "类名、方法名或完整限定类名。" }),
284
+ config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。" })),
285
+ method: Type.Optional(Type.String({ description: "detail 模式下的可选方法过滤条件。" })),
286
+ compact: Type.Optional(Type.Boolean({ description: "支持时请求紧凑详情输出。" })),
287
+ dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
285
288
  }),
286
289
 
287
290
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -289,7 +292,7 @@ const kdCosmicApiTool = defineTool({
289
292
  const rejection = rejectNonCosmic(profile);
290
293
  if (rejection) return { content: [{ type: "text", text: rejection }], details: { rejected: true, product: profile.product } };
291
294
  if (!["search", "search-method", "detail"].includes(params.mode)) {
292
- return { content: [{ type: "text", text: "mode must be one of: search, search-method, detail" }], details: { error: "invalid-mode" } };
295
+ return { content: [{ type: "text", text: "mode 必须是 searchsearch-method detail" }], details: { error: "invalid-mode" } };
293
296
  }
294
297
  return runOrDryRun(
295
298
  cosmicApiCommand(ctx.cwd, {
@@ -305,17 +308,17 @@ const kdCosmicApiTool = defineTool({
305
308
 
306
309
  const kdSdkSignatureTool = defineTool({
307
310
  name: "kd_sdk_signature",
308
- label: "KD SDK Signature",
311
+ label: "KD SDK 签名",
309
312
  description:
310
- "Inspect method/type signatures from SDK jars or dlls actually present in the current project. Prefer this over bundled knowledge for factual API signatures.",
313
+ "从当前项目真实存在的 SDK jar dll 中检查方法/类型签名。涉及 API 签名事实时,优先使用它而不是随包知识库。",
311
314
  parameters: Type.Object({
312
- product: Type.Optional(Type.String({ description: "Kingdee product. Used to derive Java or C# when language is omitted." })),
313
- language: Type.Optional(Type.String({ description: "java or csharp. Defaults from product profile." })),
314
- query: Type.Optional(Type.String({ description: "Class/type keyword to search, for example QueryServiceHelper or DynamicObject." })),
315
- className: Type.Optional(Type.String({ description: "Fully qualified Java/C# type name when known." })),
316
- method: Type.Optional(Type.String({ description: "Optional method/property name filter within the matched class/type. Does not scan all methods globally." })),
317
- path: Type.Optional(Type.String({ description: "Optional SDK lib/bin directory or specific dependency root. Defaults to current project." })),
318
- limit: Type.Optional(Type.Number({ description: "Maximum jars/dlls/classes to inspect. Defaults to 20 result classes and 200 files." })),
315
+ product: Type.Optional(Type.String({ description: "金蝶产品。未提供 language 时用于推导 Java C#。" })),
316
+ language: Type.Optional(Type.String({ description: "java csharp。默认由产品画像推导。" })),
317
+ query: Type.Optional(Type.String({ description: "要搜索的类/类型关键词,例如 QueryServiceHelper DynamicObject" })),
318
+ className: Type.Optional(Type.String({ description: "已知时提供完整限定 Java/C# 类型名。" })),
319
+ method: Type.Optional(Type.String({ description: "在匹配类/类型内过滤方法或属性;不会全局扫描所有方法。" })),
320
+ path: Type.Optional(Type.String({ description: "可选 SDK lib/bin 目录或依赖根路径。默认从当前项目查找。" })),
321
+ limit: Type.Optional(Type.Number({ description: "最大检查 jar/dll/class 数量。默认 20 个结果类、200 个文件。" })),
319
322
  }),
320
323
 
321
324
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -329,21 +332,23 @@ const kdSdkSignatureTool = defineTool({
329
332
  path: params.path,
330
333
  limit: params.limit,
331
334
  });
335
+ const text = formatSdkSignatureResult(result);
336
+ const evidencePath = result.exitCode === 0 ? writeSdkSignatureEvidence(ctx.cwd, text) : undefined;
332
337
  return {
333
- content: [{ type: "text", text: formatSdkSignatureResult(result) }],
334
- details: { product: profile.product, ...result },
338
+ content: [{ type: "text", text: evidencePath ? `${text}\n\n已写入 SDK 签名证据:${evidencePath}` : text }],
339
+ details: { product: profile.product, evidencePath, ...result },
335
340
  };
336
341
  },
337
342
  });
338
343
 
339
344
  const kdKsqlLintTool = defineTool({
340
345
  name: "kd_ksql_lint",
341
- label: "KD KSQL Lint",
342
- description: "Run the official ok-ksql lint script against a generated KSQL/SQL file.",
346
+ label: "KD KSQL 检查",
347
+ description: "对生成的 KSQL/SQL 文件运行官方 ok-ksql lint 检查。",
343
348
  parameters: Type.Object({
344
- product: Type.String({ description: "Cosmic-family product: cangqiong, xinghan, flagship, or cosmic." }),
345
- path: Type.String({ description: "SQL/KSQL file path, relative to workspace or absolute." }),
346
- dryRun: Type.Optional(Type.Boolean({ description: "Return the command without executing it." })),
349
+ product: Type.String({ description: "Cosmic 家族产品:cangqiongxinghanflagship cosmic" }),
350
+ path: Type.String({ description: "SQL/KSQL 文件路径,可为工作区相对路径或绝对路径。" }),
351
+ dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
347
352
  }),
348
353
 
349
354
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -356,12 +361,12 @@ const kdKsqlLintTool = defineTool({
356
361
 
357
362
  const kdBuildTool = defineTool({
358
363
  name: "kd_build",
359
- label: "KD Build",
360
- description: "Run or dry-run a product-aware Kingdee build command for Cosmic Java or enterprise C# projects.",
364
+ label: "KD 构建",
365
+ description: "按产品画像运行或预览金蝶构建命令,支持 Cosmic Java 和企业版 C# 项目。",
361
366
  parameters: Type.Object({
362
- product: Type.String({ description: "Kingdee product: cangqiong, xinghan, flagship, cosmic, or enterprise." }),
363
- target: Type.Optional(Type.String({ description: "Optional Gradle/Maven task for Java or .sln/.csproj path for C#." })),
364
- dryRun: Type.Optional(Type.Boolean({ description: "Return the build command without executing it." })),
367
+ product: Type.String({ description: "金蝶产品:cangqiongxinghanflagshipcosmic enterprise" }),
368
+ target: Type.Optional(Type.String({ description: "Java 可提供 Gradle/Maven task;C# 可提供 .sln/.csproj 路径。" })),
369
+ dryRun: Type.Optional(Type.Boolean({ description: "只返回构建命令,不实际执行。" })),
365
370
  }),
366
371
 
367
372
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -380,12 +385,12 @@ const kdBuildTool = defineTool({
380
385
 
381
386
  const kdDebugTool = defineTool({
382
387
  name: "kd_debug",
383
- label: "KD Debug",
384
- description: "Analyze Kingdee build/runtime logs or stack traces and map them to likely causes and next checks.",
388
+ label: "KD 调试",
389
+ description: "分析金蝶构建/运行日志或堆栈,给出可能原因和下一步检查建议。",
385
390
  parameters: Type.Object({
386
- text: Type.Optional(Type.String({ description: "Log text or stack trace. Use this or path." })),
387
- path: Type.Optional(Type.String({ description: "Path to a log file. Use this or text." })),
388
- product: Type.Optional(Type.String({ description: "Optional product hint for context." })),
391
+ text: Type.Optional(Type.String({ description: "日志文本或堆栈。与 path 二选一。" })),
392
+ path: Type.Optional(Type.String({ description: "日志文件路径。与 text 二选一。" })),
393
+ product: Type.Optional(Type.String({ description: "可选产品提示,用于补充上下文。" })),
389
394
  }),
390
395
 
391
396
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -397,7 +402,7 @@ const kdDebugTool = defineTool({
397
402
  content: [
398
403
  {
399
404
  type: "text",
400
- text: `Source: ${input.source}\nProduct: ${profile.product}/${profile.platform}/${profile.techStack}\n\n${formatDebugFindings(findings)}`,
405
+ text: `来源:${input.source}\n产品:${profile.product}/${profile.platform}/${profile.techStack}\n\n${formatDebugFindings(findings)}`,
401
406
  },
402
407
  ],
403
408
  details: { source: input.source, product: profile.product, findings },
@@ -423,3 +428,27 @@ export default function (pi: ExtensionAPI) {
423
428
  pi.registerTool(kdBuildTool);
424
429
  pi.registerTool(kdDebugTool);
425
430
  }
431
+
432
+ function writeSdkSignatureEvidence(cwd: string, content: string): string | undefined {
433
+ const run = readActiveRun(cwd);
434
+ if (!run) return undefined;
435
+
436
+ const path = runArtifactPath(cwd, run, SDK_SIGNATURE_EVIDENCE);
437
+ mkdirSync(dirname(path), { recursive: true });
438
+ writeFileSync(
439
+ path,
440
+ [
441
+ "# SDK 签名证据",
442
+ "",
443
+ `- 生成时间:${new Date().toISOString()}`,
444
+ "- 来源:kd_sdk_signature 当前项目本地 SDK jar/dll",
445
+ "",
446
+ "```text",
447
+ content.trim(),
448
+ "```",
449
+ "",
450
+ ].join("\n"),
451
+ "utf8",
452
+ );
453
+ return path;
454
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kcode-pi",
3
- "version": "0.1.14",
4
- "description": "Kingdee-specific package and harness for Pi Coding Agent",
3
+ "version": "0.1.16",
4
+ "description": "面向金蝶开发的 Pi Coding Agent 启动器、工具包和 Harness 工作流",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "bin": {
@@ -1,11 +1,11 @@
1
1
  ---
2
- description: Start or refine the Kingdee Harness Engineering discussion phase.
2
+ description: 开始或继续金蝶 Harness 工作流的需求讨论阶段。
3
3
  ---
4
4
 
5
- Use the `kd-discuss` skill.
5
+ 使用 `kd-discuss` skill
6
6
 
7
- Start or update the active Kingdee harness run for this request:
7
+ 围绕本次请求开始或更新当前金蝶 Harness run
8
8
 
9
9
  {{args}}
10
10
 
11
- Capture business goal, Kingdee product/version, tech stack, plugin type, target object, out-of-scope items, and open questions in `CONTEXT.md`.
11
+ 请把业务目标、金蝶产品/版本、技术栈、插件类型、目标单据/表单/实体、非目标范围和待确认问题记录到 `CONTEXT.md`。
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Execute the active Kingdee plan under Harness Engineering gates.
2
+ description: Harness 门禁约束下执行当前金蝶实施计划。
3
3
  ---
4
4
 
5
- Use the `kd-execute` skill.
5
+ 使用 `kd-execute` skill
6
6
 
7
- Before editing code, check the active run with `kd_plan_status`. If `PLAN.md` is missing or the gate is blocked, stop and explain the missing artifact. Otherwise implement only the approved plan and update `EXECUTION.md`.
7
+ 编辑代码前,先使用 `kd_plan_status` 检查当前 run。如果缺少 `PLAN.md`、`evidence/sdk-signature.md` 或门禁被阻塞,必须停止并说明缺少的文档或证据。通过后只实现 `PLAN.md` 批准的内容,并更新 `EXECUTION.md`。禁止凭记忆、模型知识或随包知识库猜 SDK 方法签名。
8
8
 
9
- Extra user guidance:
9
+ 用户补充说明:
10
10
 
11
11
  {{args}}
12
-
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Draft the Kingdee implementation plan artifact for the active harness run.
2
+ description: 为当前金蝶 Harness run 编写实施计划。
3
3
  ---
4
4
 
5
- Use the `kd-plan` skill.
5
+ 使用 `kd-plan` skill
6
6
 
7
- Read `CONTEXT.md` and `SPEC.md`, then draft or update `PLAN.md`. Include files to inspect, files likely to edit, required Kingdee lookups, validation commands, and rollback notes.
7
+ 读取 `CONTEXT.md` `SPEC.md`,编写或更新 `PLAN.md`。必须包含已检查的项目结构、需要查看的文件、预计修改的真实路径、必须查证的金蝶 API/元数据、SDK 签名证据、验证命令和回滚说明。Java/C# SDK 方法签名必须来自 `kd_sdk_signature` 当前项目 jar/dll、项目构建输出或官方元数据,不能凭记忆猜。
8
8
 
9
- Extra user guidance:
9
+ 用户补充说明:
10
10
 
11
11
  {{args}}
12
-
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Prepare Kingdee delivery summary after verification.
2
+ description: 验证后整理金蝶交付摘要。
3
3
  ---
4
4
 
5
- Use the `kd-ship` skill.
5
+ 使用 `kd-ship` skill
6
6
 
7
- Read the active run artifacts and produce `SHIP.md` with changed behavior, verification evidence, residual risks, and follow-up tasks. Do not ship if `VERIFY.md` is missing.
7
+ 读取当前 run 的阶段文档,生成 `SHIP.md`,说明行为变化、验证证据、残余风险和后续事项。缺少 `VERIFY.md` 时不得进入交付总结。
8
8
 
9
- Extra user guidance:
9
+ 用户补充说明:
10
10
 
11
11
  {{args}}
12
-
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Draft the Kingdee specification artifact for the active harness run.
2
+ description: 为当前金蝶 Harness run 编写需求规格文档。
3
3
  ---
4
4
 
5
- Use the `kd-spec` skill.
5
+ 使用 `kd-spec` skill
6
6
 
7
- Read the active Kingdee run context and draft or update `SPEC.md`. Include acceptance criteria, lifecycle hooks, data objects, exception behavior, performance constraints, assumptions, and risks.
7
+ 读取当前金蝶 run 的上下文,编写或更新 `SPEC.md`。必须包含验收标准、生命周期/扩展点、数据对象、异常行为、性能约束、假设和风险。
8
8
 
9
- Extra user guidance:
9
+ 用户补充说明:
10
10
 
11
11
  {{args}}
12
-
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Verify the active Kingdee implementation and collect evidence.
2
+ description: 验证当前金蝶实现并收集证据。
3
3
  ---
4
4
 
5
- Use the `kd-verify` skill.
5
+ 使用 `kd-verify` skill
6
6
 
7
- Run the planned validation commands, collect evidence, run available checks, and update `VERIFY.md`. If anything cannot run, record the exact blocker and residual risk.
7
+ 执行计划中的验证命令,收集证据,运行可用检查,并更新 `VERIFY.md`。如果某项验证无法运行,记录具体阻塞原因和残余风险。
8
8
 
9
- Extra user guidance:
9
+ 用户补充说明:
10
10
 
11
11
  {{args}}
12
-
@@ -59,33 +59,33 @@ export function generateProjectContext(cwd: string): string {
59
59
  const sourceSamples = scan.files.filter((file) => SOURCE_EXTENSIONS.has(extname(file).toLowerCase())).slice(0, MAX_SOURCE_SAMPLES);
60
60
 
61
61
  return [
62
- "# KCode Project Context",
62
+ "# KCode 项目上下文",
63
63
  "",
64
- `- Project root: ${cwd}`,
65
- `- Project name: ${basename(cwd)}`,
66
- `- Generated at: ${new Date().toISOString()}`,
64
+ `- 项目根目录:${cwd}`,
65
+ `- 项目名称:${basename(cwd)}`,
66
+ `- 生成时间:${new Date().toISOString()}`,
67
67
  "",
68
- "## Persistent Rules",
68
+ "## 持久规则",
69
69
  "",
70
- "- This file is project memory for KCode. Read it before planning or editing code.",
71
- "- Do not create demo/sample/scaffold code for business requirements.",
72
- "- Do not assume module layout. Follow the actual paths below and verify target files before editing.",
73
- "- Use project-relative paths when calling file tools. On Windows, do not rewrite paths to /mnt/<drive>/... or /<drive>/...; use Windows paths only when an absolute path is necessary.",
74
- "- If this file is stale, regenerate with `kcode context --refresh` before planning.",
75
- "- Write product code only after the Harness reaches `execute` and PLAN.md names the real target path.",
70
+ "- 本文件是 KCode 的项目记忆,计划或编辑代码前必须读取。",
71
+ "- 业务需求不得生成 demo/sample/scaffold 代码。",
72
+ "- 不要假设模块结构。必须基于下方真实路径,并在编辑前确认目标文件。",
73
+ "- 调用文件工具时优先使用项目相对路径。在 Windows 中,不要把路径改写为 /mnt/<drive>/... /<drive>/...;只有确需绝对路径时才使用 Windows 路径。",
74
+ "- 如果本文件过期,计划前先运行 `kcode context --refresh` 重新生成。",
75
+ "- 只有 Harness 进入 `execute` PLAN.md 写明真实目标路径后,才能写产品代码。",
76
76
  "",
77
- "## Layout Summary",
77
+ "## 项目结构摘要",
78
78
  "",
79
- `- Has code directory: ${codeDir ? "yes" : "no"}`,
80
- `- Likely source roots: ${formatList(likelyRoots)}`,
81
- `- Build files: ${formatList(buildFiles)}`,
82
- `- Detected modules: ${formatList(modules)}`,
79
+ `- 是否存在 code 目录:${codeDir ? "" : ""}`,
80
+ `- 可能的源码根:${formatList(likelyRoots)}`,
81
+ `- 构建文件:${formatList(buildFiles)}`,
82
+ `- 识别到的模块:${formatList(modules)}`,
83
83
  "",
84
- "## Source Samples",
84
+ "## 源码样例",
85
85
  "",
86
86
  formatBlockList(sourceSamples),
87
87
  "",
88
- "## Top-Level Directories",
88
+ "## 顶层目录",
89
89
  "",
90
90
  formatBlockList(scan.directories.filter((dir) => !dir.includes("/") && !dir.includes("\\")).sort()),
91
91
  "",
@@ -202,11 +202,11 @@ function isSolutionOrProject(file: string): boolean {
202
202
  }
203
203
 
204
204
  function formatList(values: string[]): string {
205
- return values.length > 0 ? values.join(", ") : "none detected";
205
+ return values.length > 0 ? values.join(", ") : "未识别";
206
206
  }
207
207
 
208
208
  function formatBlockList(values: string[]): string {
209
- if (values.length === 0) return "- none detected";
209
+ if (values.length === 0) return "- 未识别";
210
210
  return values.map((value) => `- ${value}`).join("\n");
211
211
  }
212
212