kcode-pi 0.1.13 → 0.1.15
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 +47 -27
- package/dist/cli/kcode.d.ts +2 -1
- package/dist/cli/kcode.js +104 -7
- package/dist/context/project-context.js +20 -20
- package/docs/DEVELOPMENT.md +2 -5
- package/extensions/kingdee-harness.ts +80 -80
- package/extensions/kingdee-header.ts +16 -16
- package/extensions/kingdee-tools.ts +82 -82
- package/package.json +3 -2
- package/prompts/kd-discuss.md +4 -4
- package/prompts/kd-execute.md +4 -5
- package/prompts/kd-plan.md +4 -5
- package/prompts/kd-ship.md +4 -5
- package/prompts/kd-spec.md +4 -5
- package/prompts/kd-verify.md +4 -5
- package/src/cli/kcode.ts +101 -7
- package/src/context/project-context.ts +20 -20
- package/src/harness/artifacts.ts +47 -47
- package/src/harness/format.ts +12 -12
- package/src/harness/gates.ts +5 -5
- package/src/harness/plan-steps.ts +3 -3
- package/src/harness/state.ts +4 -4
- package/src/harness/tdd-policy.ts +2 -2
- package/src/knowledge/format.ts +11 -12
- package/src/product/profile.ts +16 -16
- package/src/tools/build-debug.ts +35 -35
- package/src/tools/sdk-signature.ts +10 -10
|
@@ -74,8 +74,8 @@ function sdkLanguageForProfile(profile: ProductProfile, value: string | undefine
|
|
|
74
74
|
|
|
75
75
|
function rejectNonCosmic(profile: ProductProfile): string | undefined {
|
|
76
76
|
if (isCosmicFamily(profile)) return undefined;
|
|
77
|
-
if (profile.product === "unknown") return "
|
|
78
|
-
return
|
|
77
|
+
if (profile.product === "unknown") return "请先提供 Cosmic 家族产品:cangqiong、xinghan、flagship 或 cosmic。";
|
|
78
|
+
return `当前产品 ${profile.product} 使用 ${profile.platform}/${profile.techStack},不适用 Cosmic 官方能力。`;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
async function runOrDryRun(
|
|
@@ -87,7 +87,7 @@ async function runOrDryRun(
|
|
|
87
87
|
const command = await commandPromise;
|
|
88
88
|
if (dryRun) {
|
|
89
89
|
return {
|
|
90
|
-
content: [{ type: "text" as const, text:
|
|
90
|
+
content: [{ type: "text" as const, text: `仅展示命令,不执行:\n${command.display}` }],
|
|
91
91
|
details: { command: command.display, dryRun: true },
|
|
92
92
|
};
|
|
93
93
|
}
|
|
@@ -102,13 +102,13 @@ async function runOrDryRun(
|
|
|
102
102
|
|
|
103
103
|
const kdSearchTool = defineTool({
|
|
104
104
|
name: "kd_search",
|
|
105
|
-
label: "KD
|
|
106
|
-
description: "
|
|
105
|
+
label: "KD 搜索",
|
|
106
|
+
description: "搜索 KCode 随包金蝶知识库,包括 SDK、插件生命周期、代码模式和常见实现建议。",
|
|
107
107
|
parameters: Type.Object({
|
|
108
|
-
query: Type.String({ description: "
|
|
109
|
-
product: Type.Optional(Type.String({ description: "
|
|
110
|
-
edition: Type.Optional(Type.String({ description: "
|
|
111
|
-
limit: Type.Optional(Type.Number({ description: "
|
|
108
|
+
query: Type.String({ description: "要搜索的关键词、API、类名、表名或生命周期术语。" }),
|
|
109
|
+
product: Type.Optional(Type.String({ description: "金蝶产品:flagship、cosmic、xinghan、cangqiong 或 enterprise。" })),
|
|
110
|
+
edition: Type.Optional(Type.String({ description: "旧参数,等同于 product。优先使用 product。" })),
|
|
111
|
+
limit: Type.Optional(Type.Number({ description: "最大结果数,默认 5。" })),
|
|
112
112
|
}),
|
|
113
113
|
|
|
114
114
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
@@ -117,15 +117,15 @@ const kdSearchTool = defineTool({
|
|
|
117
117
|
if (!scopes) {
|
|
118
118
|
const guidance =
|
|
119
119
|
profile.product === "unknown"
|
|
120
|
-
? "
|
|
121
|
-
: "
|
|
120
|
+
? "请先提供 product,例如 flagship、enterprise、cosmic、xinghan 或 cangqiong。"
|
|
121
|
+
: "当前产品画像未配置可搜索知识范围。";
|
|
122
122
|
return {
|
|
123
123
|
content: [
|
|
124
124
|
{
|
|
125
125
|
type: "text",
|
|
126
126
|
text: [
|
|
127
|
-
|
|
128
|
-
"
|
|
127
|
+
`产品画像:${profile.product}/${profile.techStack}/${profile.language}`,
|
|
128
|
+
"KCode 随包知识库搜索需要明确产品画像。",
|
|
129
129
|
guidance,
|
|
130
130
|
].join("\n"),
|
|
131
131
|
},
|
|
@@ -145,12 +145,12 @@ const kdSearchTool = defineTool({
|
|
|
145
145
|
|
|
146
146
|
const kdTableTool = defineTool({
|
|
147
147
|
name: "kd_table",
|
|
148
|
-
label: "KD
|
|
149
|
-
description: "
|
|
148
|
+
label: "KD 表结构",
|
|
149
|
+
description: "按表名查询 KCode 随包金蝶表结构,查询结果受产品画像约束。",
|
|
150
150
|
parameters: Type.Object({
|
|
151
|
-
table: Type.String({ description: "
|
|
152
|
-
product: Type.Optional(Type.String({ description: "
|
|
153
|
-
edition: Type.Optional(Type.String({ description: "
|
|
151
|
+
table: Type.String({ description: "表名,例如 T_PUR_POORDER。" }),
|
|
152
|
+
product: Type.Optional(Type.String({ description: "金蝶产品:flagship、cosmic、xinghan、cangqiong 或 enterprise。" })),
|
|
153
|
+
edition: Type.Optional(Type.String({ description: "旧参数,等同于 product。优先使用 product。" })),
|
|
154
154
|
}),
|
|
155
155
|
|
|
156
156
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
@@ -159,15 +159,15 @@ const kdTableTool = defineTool({
|
|
|
159
159
|
if (!edition) {
|
|
160
160
|
const guidance =
|
|
161
161
|
profile.product === "unknown"
|
|
162
|
-
? "
|
|
163
|
-
: "
|
|
162
|
+
? "请先提供 product。不要跨金蝶产品族猜测表结构。"
|
|
163
|
+
: "没有元数据查证前,不要把旗舰版/企业版表结构假设复用到苍穹/星瀚/Cosmic。";
|
|
164
164
|
return {
|
|
165
165
|
content: [
|
|
166
166
|
{
|
|
167
167
|
type: "text",
|
|
168
168
|
text: [
|
|
169
|
-
|
|
170
|
-
"
|
|
169
|
+
`产品画像:${profile.product}/${profile.techStack}/${profile.language}`,
|
|
170
|
+
"KCode 随包表结构目前主要覆盖旗舰版和企业版。",
|
|
171
171
|
guidance,
|
|
172
172
|
].join("\n"),
|
|
173
173
|
},
|
|
@@ -186,14 +186,14 @@ const kdTableTool = defineTool({
|
|
|
186
186
|
|
|
187
187
|
const kdCheckTool = defineTool({
|
|
188
188
|
name: "kd_check",
|
|
189
|
-
label: "KD
|
|
189
|
+
label: "KD 检查",
|
|
190
190
|
description:
|
|
191
|
-
"
|
|
191
|
+
"检查金蝶 Java/C#/Python 插件代码中的魔法值、命名问题、循环内 DB 调用和空 catch 等问题。",
|
|
192
192
|
parameters: Type.Object({
|
|
193
|
-
code: Type.Optional(Type.String({ description: "
|
|
194
|
-
path: Type.Optional(Type.String({ description: "
|
|
195
|
-
product: Type.Optional(Type.String({ description: "
|
|
196
|
-
language: Type.Optional(Type.String({ description: "
|
|
193
|
+
code: Type.Optional(Type.String({ description: "要检查的源码。与 path 二选一。" })),
|
|
194
|
+
path: Type.Optional(Type.String({ description: "要检查的源码文件路径。与 code 二选一。" })),
|
|
195
|
+
product: Type.Optional(Type.String({ description: "金蝶产品。未提供 language 时用于推导 Java 或 C#。" })),
|
|
196
|
+
language: Type.Optional(Type.String({ description: "语言:java、csharp 或 python。会覆盖产品推导结果。" })),
|
|
197
197
|
}),
|
|
198
198
|
|
|
199
199
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -210,7 +210,7 @@ const kdCheckTool = defineTool({
|
|
|
210
210
|
|
|
211
211
|
if (!code) {
|
|
212
212
|
return {
|
|
213
|
-
content: [{ type: "text", text: "
|
|
213
|
+
content: [{ type: "text", text: "kd_check 需要提供 code 或 path。" }],
|
|
214
214
|
details: { error: "missing-code-or-path" },
|
|
215
215
|
};
|
|
216
216
|
}
|
|
@@ -220,7 +220,7 @@ const kdCheckTool = defineTool({
|
|
|
220
220
|
content: [
|
|
221
221
|
{
|
|
222
222
|
type: "text",
|
|
223
|
-
text:
|
|
223
|
+
text: `来源:${source}\n产品:${profile.product}/${profile.techStack}\n语言:${language}\n\n${formatCheckResults(results)}`,
|
|
224
224
|
},
|
|
225
225
|
],
|
|
226
226
|
details: { source, product: profile.product, language, issues: results },
|
|
@@ -230,12 +230,12 @@ const kdCheckTool = defineTool({
|
|
|
230
230
|
|
|
231
231
|
const kdCosmicConfigTool = defineTool({
|
|
232
232
|
name: "kd_cosmic_config",
|
|
233
|
-
label: "KD Cosmic
|
|
234
|
-
description: "
|
|
233
|
+
label: "KD Cosmic 配置",
|
|
234
|
+
description: "运行 Cosmic 家族金蝶产品的官方能力配置预检查。",
|
|
235
235
|
parameters: Type.Object({
|
|
236
|
-
product: Type.String({ description: "Cosmic
|
|
237
|
-
config: Type.Optional(Type.String({ description: "
|
|
238
|
-
dryRun: Type.Optional(Type.Boolean({ description: "
|
|
236
|
+
product: Type.String({ description: "Cosmic 家族产品:cangqiong、xinghan、flagship 或 cosmic。" }),
|
|
237
|
+
config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。默认按当前工作目录解析。" })),
|
|
238
|
+
dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
|
|
239
239
|
}),
|
|
240
240
|
|
|
241
241
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -248,18 +248,18 @@ const kdCosmicConfigTool = defineTool({
|
|
|
248
248
|
|
|
249
249
|
const kdCosmicMetadataTool = defineTool({
|
|
250
250
|
name: "kd_cosmic_metadata",
|
|
251
|
-
label: "KD Cosmic
|
|
252
|
-
description: "
|
|
251
|
+
label: "KD Cosmic 元数据",
|
|
252
|
+
description: "查询官方 Cosmic 表单/单据元数据,包括字段、枚举值、操作和 SQL 表信息。",
|
|
253
253
|
parameters: Type.Object({
|
|
254
|
-
product: Type.String({ description: "Cosmic
|
|
255
|
-
form: Type.String({ description: "Form ID
|
|
256
|
-
config: Type.Optional(Type.String({ description: "
|
|
257
|
-
fuzzy: Type.Optional(Type.String({ description: "
|
|
258
|
-
typeFilter: Type.Optional(Type.String({ description: "
|
|
259
|
-
sql: Type.Optional(Type.Boolean({ description: "
|
|
260
|
-
op: Type.Optional(Type.Boolean({ description: "
|
|
261
|
-
showDetail: Type.Optional(Type.Boolean({ description: "
|
|
262
|
-
dryRun: Type.Optional(Type.Boolean({ description: "
|
|
254
|
+
product: Type.String({ description: "Cosmic 家族产品:cangqiong、xinghan、flagship 或 cosmic。" }),
|
|
255
|
+
form: Type.String({ description: "Form ID、单据 ID 或中文单据名;多个目标可用逗号分隔。" }),
|
|
256
|
+
config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。" })),
|
|
257
|
+
fuzzy: Type.Optional(Type.String({ description: "可选字段关键词,用空格或逗号分隔。" })),
|
|
258
|
+
typeFilter: Type.Optional(Type.String({ description: "可选字段类型正则,例如 combo|check 或 decimal。" })),
|
|
259
|
+
sql: Type.Optional(Type.Boolean({ description: "是否包含数据库表和字段信息。" })),
|
|
260
|
+
op: Type.Optional(Type.Boolean({ description: "是否显示表单/单据操作。" })),
|
|
261
|
+
showDetail: Type.Optional(Type.Boolean({ description: "是否显示详细元数据输出。" })),
|
|
262
|
+
dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
|
|
263
263
|
}),
|
|
264
264
|
|
|
265
265
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -273,15 +273,15 @@ const kdCosmicMetadataTool = defineTool({
|
|
|
273
273
|
const kdCosmicApiTool = defineTool({
|
|
274
274
|
name: "kd_cosmic_api",
|
|
275
275
|
label: "KD Cosmic API",
|
|
276
|
-
description: "
|
|
276
|
+
description: "查询随包 Cosmic API 知识,获取类和方法线索;最终签名事实应以 kd_sdk_signature 或项目构建输出为准。",
|
|
277
277
|
parameters: Type.Object({
|
|
278
|
-
product: Type.String({ description: "Cosmic
|
|
279
|
-
mode: Type.String({ description: "search
|
|
280
|
-
query: Type.String({ description: "
|
|
281
|
-
config: Type.Optional(Type.String({ description: "
|
|
282
|
-
method: Type.Optional(Type.String({ description: "
|
|
283
|
-
compact: Type.Optional(Type.Boolean({ description: "
|
|
284
|
-
dryRun: Type.Optional(Type.Boolean({ description: "
|
|
278
|
+
product: Type.String({ description: "Cosmic 家族产品:cangqiong、xinghan、flagship 或 cosmic。" }),
|
|
279
|
+
mode: Type.String({ description: "查询模式:search、search-method 或 detail。" }),
|
|
280
|
+
query: Type.String({ description: "类名、方法名或完整限定类名。" }),
|
|
281
|
+
config: Type.Optional(Type.String({ description: "可选 ok-cosmic.json 路径。" })),
|
|
282
|
+
method: Type.Optional(Type.String({ description: "detail 模式下的可选方法过滤条件。" })),
|
|
283
|
+
compact: Type.Optional(Type.Boolean({ description: "支持时请求紧凑详情输出。" })),
|
|
284
|
+
dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
|
|
285
285
|
}),
|
|
286
286
|
|
|
287
287
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -289,7 +289,7 @@ const kdCosmicApiTool = defineTool({
|
|
|
289
289
|
const rejection = rejectNonCosmic(profile);
|
|
290
290
|
if (rejection) return { content: [{ type: "text", text: rejection }], details: { rejected: true, product: profile.product } };
|
|
291
291
|
if (!["search", "search-method", "detail"].includes(params.mode)) {
|
|
292
|
-
return { content: [{ type: "text", text: "mode
|
|
292
|
+
return { content: [{ type: "text", text: "mode 必须是 search、search-method 或 detail。" }], details: { error: "invalid-mode" } };
|
|
293
293
|
}
|
|
294
294
|
return runOrDryRun(
|
|
295
295
|
cosmicApiCommand(ctx.cwd, {
|
|
@@ -305,17 +305,17 @@ const kdCosmicApiTool = defineTool({
|
|
|
305
305
|
|
|
306
306
|
const kdSdkSignatureTool = defineTool({
|
|
307
307
|
name: "kd_sdk_signature",
|
|
308
|
-
label: "KD SDK
|
|
308
|
+
label: "KD SDK 签名",
|
|
309
309
|
description:
|
|
310
|
-
"
|
|
310
|
+
"从当前项目真实存在的 SDK jar 或 dll 中检查方法/类型签名。涉及 API 签名事实时,优先使用它而不是随包知识库。",
|
|
311
311
|
parameters: Type.Object({
|
|
312
|
-
product: Type.Optional(Type.String({ description: "
|
|
313
|
-
language: Type.Optional(Type.String({ description: "java
|
|
314
|
-
query: Type.Optional(Type.String({ description: "
|
|
315
|
-
className: Type.Optional(Type.String({ description: "
|
|
316
|
-
method: Type.Optional(Type.String({ description: "
|
|
317
|
-
path: Type.Optional(Type.String({ description: "
|
|
318
|
-
limit: Type.Optional(Type.Number({ description: "
|
|
312
|
+
product: Type.Optional(Type.String({ description: "金蝶产品。未提供 language 时用于推导 Java 或 C#。" })),
|
|
313
|
+
language: Type.Optional(Type.String({ description: "java 或 csharp。默认由产品画像推导。" })),
|
|
314
|
+
query: Type.Optional(Type.String({ description: "要搜索的类/类型关键词,例如 QueryServiceHelper 或 DynamicObject。" })),
|
|
315
|
+
className: Type.Optional(Type.String({ description: "已知时提供完整限定 Java/C# 类型名。" })),
|
|
316
|
+
method: Type.Optional(Type.String({ description: "在匹配类/类型内过滤方法或属性;不会全局扫描所有方法。" })),
|
|
317
|
+
path: Type.Optional(Type.String({ description: "可选 SDK lib/bin 目录或依赖根路径。默认从当前项目查找。" })),
|
|
318
|
+
limit: Type.Optional(Type.Number({ description: "最大检查 jar/dll/class 数量。默认 20 个结果类、200 个文件。" })),
|
|
319
319
|
}),
|
|
320
320
|
|
|
321
321
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -338,12 +338,12 @@ const kdSdkSignatureTool = defineTool({
|
|
|
338
338
|
|
|
339
339
|
const kdKsqlLintTool = defineTool({
|
|
340
340
|
name: "kd_ksql_lint",
|
|
341
|
-
label: "KD KSQL
|
|
342
|
-
description: "
|
|
341
|
+
label: "KD KSQL 检查",
|
|
342
|
+
description: "对生成的 KSQL/SQL 文件运行官方 ok-ksql lint 检查。",
|
|
343
343
|
parameters: Type.Object({
|
|
344
|
-
product: Type.String({ description: "Cosmic
|
|
345
|
-
path: Type.String({ description: "SQL/KSQL
|
|
346
|
-
dryRun: Type.Optional(Type.Boolean({ description: "
|
|
344
|
+
product: Type.String({ description: "Cosmic 家族产品:cangqiong、xinghan、flagship 或 cosmic。" }),
|
|
345
|
+
path: Type.String({ description: "SQL/KSQL 文件路径,可为工作区相对路径或绝对路径。" }),
|
|
346
|
+
dryRun: Type.Optional(Type.Boolean({ description: "只返回命令,不实际执行。" })),
|
|
347
347
|
}),
|
|
348
348
|
|
|
349
349
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -356,12 +356,12 @@ const kdKsqlLintTool = defineTool({
|
|
|
356
356
|
|
|
357
357
|
const kdBuildTool = defineTool({
|
|
358
358
|
name: "kd_build",
|
|
359
|
-
label: "KD
|
|
360
|
-
description: "
|
|
359
|
+
label: "KD 构建",
|
|
360
|
+
description: "按产品画像运行或预览金蝶构建命令,支持 Cosmic Java 和企业版 C# 项目。",
|
|
361
361
|
parameters: Type.Object({
|
|
362
|
-
product: Type.String({ description: "
|
|
363
|
-
target: Type.Optional(Type.String({ description: "
|
|
364
|
-
dryRun: Type.Optional(Type.Boolean({ description: "
|
|
362
|
+
product: Type.String({ description: "金蝶产品:cangqiong、xinghan、flagship、cosmic 或 enterprise。" }),
|
|
363
|
+
target: Type.Optional(Type.String({ description: "Java 可提供 Gradle/Maven task;C# 可提供 .sln/.csproj 路径。" })),
|
|
364
|
+
dryRun: Type.Optional(Type.Boolean({ description: "只返回构建命令,不实际执行。" })),
|
|
365
365
|
}),
|
|
366
366
|
|
|
367
367
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -380,12 +380,12 @@ const kdBuildTool = defineTool({
|
|
|
380
380
|
|
|
381
381
|
const kdDebugTool = defineTool({
|
|
382
382
|
name: "kd_debug",
|
|
383
|
-
label: "KD
|
|
384
|
-
description: "
|
|
383
|
+
label: "KD 调试",
|
|
384
|
+
description: "分析金蝶构建/运行日志或堆栈,给出可能原因和下一步检查建议。",
|
|
385
385
|
parameters: Type.Object({
|
|
386
|
-
text: Type.Optional(Type.String({ description: "
|
|
387
|
-
path: Type.Optional(Type.String({ description: "
|
|
388
|
-
product: Type.Optional(Type.String({ description: "
|
|
386
|
+
text: Type.Optional(Type.String({ description: "日志文本或堆栈。与 path 二选一。" })),
|
|
387
|
+
path: Type.Optional(Type.String({ description: "日志文件路径。与 text 二选一。" })),
|
|
388
|
+
product: Type.Optional(Type.String({ description: "可选产品提示,用于补充上下文。" })),
|
|
389
389
|
}),
|
|
390
390
|
|
|
391
391
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -396,9 +396,9 @@ const kdDebugTool = defineTool({
|
|
|
396
396
|
return {
|
|
397
397
|
content: [
|
|
398
398
|
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
type: "text",
|
|
400
|
+
text: `来源:${input.source}\n产品:${profile.product}/${profile.platform}/${profile.techStack}\n\n${formatDebugFindings(findings)}`,
|
|
401
|
+
},
|
|
402
402
|
],
|
|
403
403
|
details: { source: input.source, product: profile.product, findings },
|
|
404
404
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kcode-pi",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.15",
|
|
4
|
+
"description": "面向金蝶开发的 Pi Coding Agent 启动器、工具包和 Harness 工作流",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bin": {
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"smoke:sdk-signature": "tsx scripts/smoke-sdk-signature.ts",
|
|
65
65
|
"smoke:package": "tsx scripts/smoke-package.ts",
|
|
66
66
|
"smoke:kcode-cli": "tsx scripts/smoke-kcode-cli.ts",
|
|
67
|
+
"release:check": "tsx scripts/release-check.ts",
|
|
67
68
|
"kcode": "tsx scripts/kcode.ts",
|
|
68
69
|
"prepack": "npm run build:cli && npm run smoke:package"
|
|
69
70
|
},
|
package/prompts/kd-discuss.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 开始或继续金蝶 Harness 工作流的需求讨论阶段。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-discuss` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
围绕本次请求开始或更新当前金蝶 Harness run:
|
|
8
8
|
|
|
9
9
|
{{args}}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
请把业务目标、金蝶产品/版本、技术栈、插件类型、目标单据/表单/实体、非目标范围和待确认问题记录到 `CONTEXT.md`。
|
package/prompts/kd-execute.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 在 Harness 门禁约束下执行当前金蝶实施计划。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-execute` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
编辑代码前,先使用 `kd_plan_status` 检查当前 run。如果缺少 `PLAN.md` 或门禁被阻塞,必须停止并说明缺少的文档或证据。通过后只实现 `PLAN.md` 批准的内容,并更新 `EXECUTION.md`。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
用户补充说明:
|
|
10
10
|
|
|
11
11
|
{{args}}
|
|
12
|
-
|
package/prompts/kd-plan.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 为当前金蝶 Harness run 编写实施计划。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-plan` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
读取 `CONTEXT.md` 和 `SPEC.md`,编写或更新 `PLAN.md`。必须包含已检查的项目结构、需要查看的文件、预计修改的真实路径、必须查证的金蝶 API/元数据、验证命令和回滚说明。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
用户补充说明:
|
|
10
10
|
|
|
11
11
|
{{args}}
|
|
12
|
-
|
package/prompts/kd-ship.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 验证后整理金蝶交付摘要。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-ship` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
读取当前 run 的阶段文档,生成 `SHIP.md`,说明行为变化、验证证据、残余风险和后续事项。缺少 `VERIFY.md` 时不得进入交付总结。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
用户补充说明:
|
|
10
10
|
|
|
11
11
|
{{args}}
|
|
12
|
-
|
package/prompts/kd-spec.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 为当前金蝶 Harness run 编写需求规格文档。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-spec` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
读取当前金蝶 run 的上下文,编写或更新 `SPEC.md`。必须包含验收标准、生命周期/扩展点、数据对象、异常行为、性能约束、假设和风险。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
用户补充说明:
|
|
10
10
|
|
|
11
11
|
{{args}}
|
|
12
|
-
|
package/prompts/kd-verify.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 验证当前金蝶实现并收集证据。
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
使用 `kd-verify` skill。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
执行计划中的验证命令,收集证据,运行可用检查,并更新 `VERIFY.md`。如果某项验证无法运行,记录具体阻塞原因和残余风险。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
用户补充说明:
|
|
10
10
|
|
|
11
11
|
{{args}}
|
|
12
|
-
|
package/src/cli/kcode.ts
CHANGED
|
@@ -37,7 +37,9 @@ export function runKcodeCli(args: string[], cwd = process.cwd()): KcodeCliResult
|
|
|
37
37
|
case "context":
|
|
38
38
|
return context(cwd, args.slice(1));
|
|
39
39
|
case "doctor":
|
|
40
|
-
return doctor(cwd);
|
|
40
|
+
return doctor(cwd, args.slice(1));
|
|
41
|
+
case "repair":
|
|
42
|
+
return repair(cwd);
|
|
41
43
|
case "version":
|
|
42
44
|
case "--version":
|
|
43
45
|
case "-v":
|
|
@@ -81,32 +83,101 @@ export function context(cwd: string, args: string[]): KcodeCliResult {
|
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
export function doctor(cwd: string): KcodeCliResult {
|
|
86
|
+
export function doctor(cwd: string, args: string[] = []): KcodeCliResult {
|
|
87
|
+
const deep = args.includes("--deep");
|
|
85
88
|
const lines: string[] = [];
|
|
86
89
|
const node = spawnSync("node", ["--version"], { encoding: "utf8" });
|
|
87
90
|
const piCli = resolvePiCliCommand(["--version"]);
|
|
88
91
|
const pi = piCli ? spawnSync(piCli.command, piCli.args, { encoding: "utf8" }) : undefined;
|
|
89
92
|
const settingsPath = projectSettingsPath(cwd);
|
|
93
|
+
const projectContextPath = join(cwd, ".pi", "kd", "PROJECT_CONTEXT.md");
|
|
94
|
+
let errors = 0;
|
|
95
|
+
let warnings = 0;
|
|
90
96
|
|
|
91
97
|
lines.push(`Node:${node.status === 0 ? node.stdout.trim() : "未找到"}`);
|
|
92
98
|
lines.push(`Pi CLI:${formatPiCliStatus(piCli, pi)}`);
|
|
93
99
|
lines.push(`KCode version:${packageName}@${packageVersion}`);
|
|
94
100
|
lines.push(`KCode package:${packageRoot}`);
|
|
95
101
|
lines.push(`项目配置:${existsSync(settingsPath) ? settingsPath : "未创建,请先运行 kcode init"}`);
|
|
96
|
-
lines.push(`项目上下文:${existsSync(
|
|
102
|
+
lines.push(`项目上下文:${existsSync(projectContextPath) ? projectContextPath : "未创建,请运行 kcode context"}`);
|
|
97
103
|
|
|
98
104
|
if (existsSync(settingsPath)) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
const settingsResult = readSettingsSafe(settingsPath);
|
|
106
|
+
if (!settingsResult.ok) {
|
|
107
|
+
errors++;
|
|
108
|
+
lines.push(`[ERROR] 项目配置无法解析:${settingsResult.error}`);
|
|
109
|
+
} else {
|
|
110
|
+
const packages = settingsResult.settings.packages ?? [];
|
|
111
|
+
const hasKcode = packages.includes(normalizePath(packageRoot));
|
|
112
|
+
lines.push(`KCode package 已登记:${hasKcode ? "是" : "否"}`);
|
|
113
|
+
if (!hasKcode) warnings++;
|
|
114
|
+
if (deep) {
|
|
115
|
+
const kcodePackages = packages.filter((pkg) => isSameKcodePackage(pkg, normalizePath(packageRoot)));
|
|
116
|
+
const stalePackages = kcodePackages.filter((pkg) => normalizePath(pkg) !== normalizePath(packageRoot));
|
|
117
|
+
lines.push(`KCode package 条目数:${kcodePackages.length}`);
|
|
118
|
+
if (stalePackages.length > 0) {
|
|
119
|
+
warnings++;
|
|
120
|
+
lines.push(`[WARN] 发现旧 KCode package 路径:${stalePackages.join(" | ")}`);
|
|
121
|
+
lines.push(" 运行 kcode repair 可清理旧路径并刷新项目上下文。");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
warnings++;
|
|
102
127
|
}
|
|
103
128
|
|
|
129
|
+
if (deep) {
|
|
130
|
+
if (!isSupportedNodeVersion(process.version)) {
|
|
131
|
+
errors++;
|
|
132
|
+
lines.push(`[ERROR] 当前 Node ${process.version} 不满足要求:>=22.19.0`);
|
|
133
|
+
}
|
|
134
|
+
if (!existsSync(projectContextPath)) {
|
|
135
|
+
warnings++;
|
|
136
|
+
lines.push("[WARN] 项目上下文不存在,运行 kcode context --refresh 或 kcode repair。");
|
|
137
|
+
}
|
|
138
|
+
const npmPrefix = spawnSync("npm", ["prefix", "-g"], { encoding: "utf8" });
|
|
139
|
+
const npmRoot = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
|
|
140
|
+
lines.push(`npm prefix -g:${npmPrefix.status === 0 ? npmPrefix.stdout.trim() : "不可用"}`);
|
|
141
|
+
lines.push(`npm root -g:${npmRoot.status === 0 ? npmRoot.stdout.trim() : "不可用"}`);
|
|
142
|
+
lines.push(`active run:${existsSync(join(cwd, ".pi", "kd", "active-run.json")) ? "存在" : "无"}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (deep) lines.push(`诊断汇总:errors=${errors} warnings=${warnings}`);
|
|
146
|
+
|
|
104
147
|
return {
|
|
105
|
-
exitCode: pi?.status === 0 ? 0 : 1,
|
|
148
|
+
exitCode: pi?.status === 0 && errors === 0 ? 0 : 1,
|
|
106
149
|
output: lines.join("\n"),
|
|
107
150
|
};
|
|
108
151
|
}
|
|
109
152
|
|
|
153
|
+
export function repair(cwd: string): KcodeCliResult {
|
|
154
|
+
const settingsPath = projectSettingsPath(cwd);
|
|
155
|
+
const settingsResult = readSettingsSafe(settingsPath);
|
|
156
|
+
if (!settingsResult.ok) {
|
|
157
|
+
return {
|
|
158
|
+
exitCode: 1,
|
|
159
|
+
output: `项目配置无法解析,未自动覆盖:${settingsPath}\n原因:${settingsResult.error}`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const currentPackage = normalizePath(packageRoot);
|
|
164
|
+
const packages = (settingsResult.settings.packages ?? []).filter((pkg) => !isSameKcodePackage(pkg, currentPackage));
|
|
165
|
+
packages.unshift(currentPackage);
|
|
166
|
+
settingsResult.settings.packages = packages;
|
|
167
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
168
|
+
writeFileSync(settingsPath, `${JSON.stringify(settingsResult.settings, null, 2)}\n`, "utf8");
|
|
169
|
+
const projectContext = writeProjectContext(cwd);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
exitCode: 0,
|
|
173
|
+
output: [
|
|
174
|
+
`已修复项目级 Pi 配置:${settingsPath}`,
|
|
175
|
+
`已保留当前 KCode package:${currentPackage}`,
|
|
176
|
+
`已刷新项目上下文:${projectContext.path}`,
|
|
177
|
+
].join("\n"),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
110
181
|
export function version(): KcodeCliResult {
|
|
111
182
|
const piCli = resolvePiCliCommand(["--version"]);
|
|
112
183
|
const pi = piCli ? spawnSync(piCli.command, piCli.args, { encoding: "utf8" }) : undefined;
|
|
@@ -176,6 +247,14 @@ function readSettings(path: string): PiSettings {
|
|
|
176
247
|
return JSON.parse(readFileSync(path, "utf8")) as PiSettings;
|
|
177
248
|
}
|
|
178
249
|
|
|
250
|
+
function readSettingsSafe(path: string): { ok: true; settings: PiSettings } | { ok: false; error: string } {
|
|
251
|
+
try {
|
|
252
|
+
return { ok: true, settings: readSettings(path) };
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
179
258
|
function normalizePath(path: string): string {
|
|
180
259
|
return resolve(path);
|
|
181
260
|
}
|
|
@@ -233,6 +312,20 @@ function formatPiCliStatus(
|
|
|
233
312
|
return `${version}(${source}:${piCli.displayPath})`;
|
|
234
313
|
}
|
|
235
314
|
|
|
315
|
+
function isSupportedNodeVersion(version: string): boolean {
|
|
316
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
317
|
+
if (!match) return false;
|
|
318
|
+
const [, majorText, minorText, patchText] = match;
|
|
319
|
+
const major = Number(majorText);
|
|
320
|
+
const minor = Number(minorText);
|
|
321
|
+
const patch = Number(patchText);
|
|
322
|
+
if (major > 22) return true;
|
|
323
|
+
if (major < 22) return false;
|
|
324
|
+
if (minor > 19) return true;
|
|
325
|
+
if (minor < 19) return false;
|
|
326
|
+
return patch >= 0;
|
|
327
|
+
}
|
|
328
|
+
|
|
236
329
|
function piCliPackageVersion(piCli: PiCliCommand): string | undefined {
|
|
237
330
|
if (piCli.source !== "bundled") return undefined;
|
|
238
331
|
|
|
@@ -253,6 +346,7 @@ function helpText(): string {
|
|
|
253
346
|
" kcode init 初始化当前项目的 .pi/settings.json",
|
|
254
347
|
" kcode context 生成或刷新 .pi/kd/PROJECT_CONTEXT.md",
|
|
255
348
|
" kcode doctor 检查 Node、随包 Pi CLI、KCode package 和项目级配置",
|
|
349
|
+
" kcode repair 清理旧 KCode package 路径并刷新项目上下文",
|
|
256
350
|
" kcode version 显示 KCode、随包 Pi CLI 和 Node 版本",
|
|
257
351
|
" kcode start 初始化项目配置后启动 KCode 工作环境",
|
|
258
352
|
].join("\n");
|