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.
@@ -84,7 +84,7 @@ function sendWorkflowPrompt(pi: ExtensionAPI, ctx: ExtensionContext, run: NonNul
84
84
  return;
85
85
  }
86
86
  pi.sendUserMessage(prompt, { deliverAs: "followUp" });
87
- if (ctx.hasUI) ctx.ui.notify("KCode harness message queued.", "info");
87
+ if (ctx.hasUI) ctx.ui.notify("KCode 工作流消息已排队。", "info");
88
88
  }
89
89
 
90
90
  function workflowPromptForRun(cwd: string, run: NonNullable<ReturnType<typeof readActiveRun>>, userText: string): string {
@@ -163,8 +163,8 @@ function codeWriteBlockReason(cwd: string, path: string | undefined): string | u
163
163
 
164
164
  const kdPlanStatusTool = defineTool({
165
165
  name: "kd_plan_status",
166
- label: "KD Status",
167
- description: "Inspect the active Kingdee Harness Engineering run, current phase, required artifacts, and gate status.",
166
+ label: "KD 状态",
167
+ description: "查看当前金蝶 Harness run、当前阶段、必需文档和门禁状态。",
168
168
  parameters: Type.Object({}),
169
169
 
170
170
  async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
@@ -178,24 +178,24 @@ const kdPlanStatusTool = defineTool({
178
178
 
179
179
  const kdQuestionTool = defineTool({
180
180
  name: "kd_question",
181
- label: "KD Question",
181
+ label: "KD 问题",
182
182
  description:
183
- "Create, answer, or list structured Kingdee Harness questions. Ask exactly one short blocking question at a time; do not batch a checklist.",
183
+ "创建、回答或列出金蝶 Harness 结构化问题。每次只能问一个最阻塞的短问题,不要批量列清单。",
184
184
  parameters: Type.Object({
185
- action: Type.Optional(Type.String({ description: "ask, answer, or list. Defaults to ask." })),
186
- id: Type.Optional(Type.String({ description: "Question id when action=answer, for example Q-001." })),
187
- question: Type.Optional(Type.String({ description: "One short question to ask when action=ask. No numbered lists or multiple questions." })),
188
- answer: Type.Optional(Type.String({ description: "User answer when action=answer." })),
189
- reason: Type.Optional(Type.String({ description: "Why this question blocks or matters." })),
190
- choices: Type.Optional(Type.Array(Type.String(), { description: "Optional concrete choices for the user, maximum 3 short labels." })),
191
- blocking: Type.Optional(Type.Boolean({ description: "Whether the question blocks phase advancement. Defaults to true." })),
185
+ action: Type.Optional(Type.String({ description: "操作类型:askanswer list,默认 ask" })),
186
+ id: Type.Optional(Type.String({ description: "回答问题时的问题编号,例如 Q-001" })),
187
+ question: Type.Optional(Type.String({ description: "提问内容。只能是一个短问题,不要写编号清单或多个问题。" })),
188
+ answer: Type.Optional(Type.String({ description: "用户答案,action=answer 时使用。" })),
189
+ reason: Type.Optional(Type.String({ description: "说明为什么这个问题会阻塞当前阶段。" })),
190
+ choices: Type.Optional(Type.Array(Type.String(), { description: "可选项,最多 3 个简短选项。" })),
191
+ blocking: Type.Optional(Type.Boolean({ description: "是否阻塞阶段推进,默认 true" })),
192
192
  }),
193
193
 
194
194
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
195
195
  const run = readActiveRun(ctx.cwd);
196
196
  if (!run) {
197
197
  return {
198
- content: [{ type: "text", text: "No active Kingdee harness run. Start one with /kd-start <goal> first." }],
198
+ content: [{ type: "text", text: "当前没有 active Kingdee Harness run。请先使用 /kd-start <需求> 创建。" }],
199
199
  details: { error: "no-active-run" },
200
200
  };
201
201
  }
@@ -209,34 +209,34 @@ const kdQuestionTool = defineTool({
209
209
  if (action === "answer") {
210
210
  if (!params.id || !params.answer) {
211
211
  return {
212
- content: [{ type: "text", text: "kd_question action=answer requires id and answer." }],
212
+ content: [{ type: "text", text: "kd_question action=answer 需要同时提供 id answer" }],
213
213
  details: { error: "missing-answer-params" },
214
214
  };
215
215
  }
216
216
  const answered = answerQuestion(ctx.cwd, run, params.id, params.answer);
217
217
  if (!answered) {
218
218
  return {
219
- content: [{ type: "text", text: `Question not found: ${params.id}` }],
219
+ content: [{ type: "text", text: `未找到问题:${params.id}` }],
220
220
  details: { error: "question-not-found", id: params.id },
221
221
  };
222
222
  }
223
- appendQuestionEventToArtifact(ctx.cwd, run, [`- Answered ${answered.id}: ${answered.answer}`]);
223
+ appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
224
224
  return {
225
- content: [{ type: "text", text: `Recorded answer for ${answered.id}. Gate refreshed.` }],
225
+ content: [{ type: "text", text: `已记录 ${answered.id} 的答案,并刷新门禁。` }],
226
226
  details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate },
227
227
  };
228
228
  }
229
229
 
230
230
  if (action !== "ask") {
231
231
  return {
232
- content: [{ type: "text", text: `Unknown kd_question action: ${params.action}. Use ask, answer, or list.` }],
232
+ content: [{ type: "text", text: `未知 kd_question action:${params.action}。请使用 askanswer list。` }],
233
233
  details: { error: "unknown-action", action: params.action },
234
234
  };
235
235
  }
236
236
 
237
237
  if (!params.question?.trim()) {
238
238
  return {
239
- content: [{ type: "text", text: "kd_question action=ask requires question." }],
239
+ content: [{ type: "text", text: "kd_question action=ask 需要提供 question" }],
240
240
  details: { error: "missing-question" },
241
241
  };
242
242
  }
@@ -272,15 +272,15 @@ const kdQuestionTool = defineTool({
272
272
  if (interactiveAnswer) {
273
273
  const answered = answerQuestion(ctx.cwd, run, question.id, interactiveAnswer);
274
274
  if (answered) {
275
- appendQuestionEventToArtifact(ctx.cwd, run, [`- Answered ${answered.id}: ${answered.answer}`]);
275
+ appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
276
276
  return {
277
- content: [{ type: "text", text: `User answered ${answered.id}: ${answered.answer}` }],
277
+ content: [{ type: "text", text: `用户已回答 ${answered.id}:${answered.answer}` }],
278
278
  details: { question: answered, gate: readActiveRun(ctx.cwd)?.gate, answered: true },
279
279
  };
280
280
  }
281
281
  }
282
282
 
283
- const text = `${formatQuestionCard(question)}\n\nInteractive answer was not provided; keep this question open until the user answers.`;
283
+ const text = `${formatQuestionCard(question)}\n\n未获得交互式答案;在用户回答前,该问题会保持 open。`;
284
284
  return { content: [{ type: "text", text }], details: { question, gate: readActiveRun(ctx.cwd)?.gate, answered: false } };
285
285
  },
286
286
  });
@@ -309,7 +309,7 @@ export default function (pi: ExtensionAPI) {
309
309
  if (!run && shouldStartHarnessFromInput(event.text)) {
310
310
  run = createActiveRun(ctx.cwd, event.text);
311
311
  if (ctx.hasUI) {
312
- ctx.ui.notify(`Started Kingdee harness run: ${run.id} (${run.profile?.product}/${run.profile?.techStack})`, "info");
312
+ ctx.ui.notify(`已启动 Kingdee Harness run:${run.id}(${run.profile?.product}/${run.profile?.techStack})`, "info");
313
313
  }
314
314
  }
315
315
 
@@ -341,14 +341,14 @@ export default function (pi: ExtensionAPI) {
341
341
  });
342
342
 
343
343
  pi.registerCommand("kd-status", {
344
- description: "Show the active Kingdee harness run and gate status",
344
+ description: "查看当前 Kingdee Harness run 和门禁状态",
345
345
  handler: async (_args, ctx) => {
346
346
  ctx.ui.notify(formatStatus(ctx.cwd, readActiveRun(ctx.cwd)), "info");
347
347
  },
348
348
  });
349
349
 
350
350
  pi.registerCommand("kd-runs", {
351
- description: "List Kingdee harness runs for this project",
351
+ description: "列出当前项目的 Kingdee Harness run",
352
352
  handler: async (_args, ctx) => {
353
353
  const active = readActiveRun(ctx.cwd);
354
354
  ctx.ui.notify(formatRuns(listRuns(ctx.cwd), active?.id), "info");
@@ -356,46 +356,46 @@ export default function (pi: ExtensionAPI) {
356
356
  });
357
357
 
358
358
  pi.registerCommand("kd-gate", {
359
- description: "Refresh and show the active Kingdee harness gate",
359
+ description: "刷新并查看当前 Kingdee Harness 门禁",
360
360
  handler: async (_args, ctx) => {
361
361
  const run = requireRun(ctx.cwd);
362
362
  if (!run) {
363
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
363
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
364
364
  return;
365
365
  }
366
366
  const refreshed = refreshGate(ctx.cwd, run);
367
- ctx.ui.notify(refreshed.gate.passed ? "Gate passed" : `Gate blocked: ${refreshed.gate.reason}`, "info");
367
+ ctx.ui.notify(refreshed.gate.passed ? "门禁通过" : `门禁阻塞:${refreshed.gate.reason}`, "info");
368
368
  },
369
369
  });
370
370
 
371
371
  pi.registerCommand("kd-start", {
372
- description: "Start a Kingdee harness run: /kd-start [--product product] [--version version] <goal>",
372
+ description: "启动一个 Kingdee Harness run:/kd-start [--product 产品] [--version 版本] <需求>",
373
373
  handler: async (args, ctx) => {
374
374
  const parsed = parseStartArgs(args);
375
375
  const goal = parsed.goal;
376
376
  if (!goal) {
377
- ctx.ui.notify("Usage: /kd-start [--product product] [--version version] <goal>", "error");
377
+ ctx.ui.notify("用法:/kd-start [--product 产品] [--version 版本] <需求>", "error");
378
378
  return;
379
379
  }
380
380
 
381
381
  const run = createActiveRun(ctx.cwd, goal, parsed.product, parsed.version);
382
- ctx.ui.notify(`Started Kingdee harness run: ${run.id} (${run.profile?.product}/${run.profile?.techStack})`, "info");
382
+ ctx.ui.notify(`已启动 Kingdee Harness run:${run.id}(${run.profile?.product}/${run.profile?.techStack})`, "info");
383
383
  sendWorkflowPrompt(pi, ctx, run, `继续 KCode Harness run ${run.id}:${goal}`);
384
384
  },
385
385
  });
386
386
 
387
387
  pi.registerCommand("kd-resume", {
388
- description: "Resume the active Kingdee harness run and inject persisted context",
388
+ description: "接续当前 Kingdee Harness run,并注入已落盘上下文",
389
389
  handler: async (args, ctx) => {
390
390
  const run = requireRun(ctx.cwd);
391
391
  if (!run) {
392
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal> or /kd-runs.", "error");
392
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求> /kd-runs", "error");
393
393
  return;
394
394
  }
395
395
 
396
396
  const note = args.trim();
397
397
  const message = [
398
- `继续 KCode Harness run ${run.id}:${run.goal ?? "unknown goal"}`,
398
+ `继续 KCode Harness run ${run.id}:${run.goal ?? "未知需求"}`,
399
399
  note ? `用户补充:${note}` : undefined,
400
400
  ]
401
401
  .filter(Boolean)
@@ -405,64 +405,64 @@ export default function (pi: ExtensionAPI) {
405
405
  });
406
406
 
407
407
  pi.registerCommand("kd-switch", {
408
- description: "Switch active Kingdee harness run: /kd-switch <run-id>",
408
+ description: "切换当前 active Kingdee Harness run:/kd-switch <run-id>",
409
409
  handler: async (args, ctx) => {
410
410
  const id = args.trim();
411
411
  if (!id) {
412
- ctx.ui.notify("Usage: /kd-switch <run-id>", "error");
412
+ ctx.ui.notify("用法:/kd-switch <run-id>", "error");
413
413
  return;
414
414
  }
415
415
 
416
416
  const run = switchActiveRun(ctx.cwd, id);
417
417
  if (!run) {
418
- ctx.ui.notify(`Kingdee harness run not found: ${id}. Use /kd-runs to list runs.`, "error");
418
+ ctx.ui.notify(`未找到 Kingdee Harness run:${id}。请用 /kd-runs 查看列表。`, "error");
419
419
  return;
420
420
  }
421
421
 
422
- ctx.ui.notify(`Switched active Kingdee harness run: ${run.id} (${run.phase})`, "info");
422
+ ctx.ui.notify(`已切换 active Kingdee Harness run:${run.id}(${run.phase})`, "info");
423
423
  },
424
424
  });
425
425
 
426
426
  pi.registerCommand("kd-finish", {
427
- description: "Mark the active Kingdee harness run as done and clear active run",
427
+ description: "标记当前 Kingdee Harness run 完成,并清除 active run",
428
428
  handler: async (_args, ctx) => {
429
429
  const run = requireRun(ctx.cwd);
430
430
  if (!run) {
431
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
431
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
432
432
  return;
433
433
  }
434
434
 
435
435
  const finished = finishActiveRun(ctx.cwd, run);
436
- ctx.ui.notify(`Finished Kingdee harness run: ${finished.id}. Start the next feature with /kd-start <goal>.`, "info");
436
+ ctx.ui.notify(`已完成 Kingdee Harness run:${finished.id}。下一个功能点请使用 /kd-start <需求>。`, "info");
437
437
  },
438
438
  });
439
439
 
440
440
  pi.registerCommand("kd-product", {
441
- description: "Set the active Kingdee product profile: /kd-product <flagship|cosmic|xinghan|cangqiong|enterprise> [--version version]",
441
+ description: "设置当前金蝶产品画像:/kd-product <flagship|cosmic|xinghan|cangqiong|enterprise> [--version 版本]",
442
442
  handler: async (args, ctx) => {
443
443
  const run = requireRun(ctx.cwd);
444
444
  if (!run) {
445
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
445
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
446
446
  return;
447
447
  }
448
448
 
449
449
  const parsed = parseProductArgs(args);
450
450
  if (!parsed) {
451
- ctx.ui.notify("Usage: /kd-product <flagship|cosmic|xinghan|cangqiong|enterprise> [--version version]", "error");
451
+ ctx.ui.notify("用法:/kd-product <flagship|cosmic|xinghan|cangqiong|enterprise> [--version 版本]", "error");
452
452
  return;
453
453
  }
454
454
 
455
455
  const updated = updateProductProfile(ctx.cwd, run, parsed.product, parsed.version);
456
- ctx.ui.notify(`Product profile: ${updated.profile?.product}/${updated.profile?.techStack}/${updated.profile?.language}`, "info");
456
+ ctx.ui.notify(`产品画像:${updated.profile?.product}/${updated.profile?.techStack}/${updated.profile?.language}`, "info");
457
457
  },
458
458
  });
459
459
 
460
460
  pi.registerCommand("kd-advance", {
461
- description: `Advance Kingdee run to the next phase or a named phase: ${PHASE_ORDER.join("|")}`,
461
+ description: `推进 Kingdee run 到下一阶段,或指定阶段:${PHASE_ORDER.join("|")}`,
462
462
  handler: async (args, ctx) => {
463
463
  const run = requireRun(ctx.cwd);
464
464
  if (!run) {
465
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
465
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
466
466
  return;
467
467
  }
468
468
 
@@ -470,7 +470,7 @@ export default function (pi: ExtensionAPI) {
470
470
  let requested: KdPhase | undefined;
471
471
  if (requestedText) {
472
472
  if (!isKdPhase(requestedText)) {
473
- ctx.ui.notify(`Unknown phase "${requestedText}". Valid phases: ${PHASE_ORDER.join(", ")}`, "error");
473
+ ctx.ui.notify(`未知阶段 "${requestedText}"。可用阶段:${PHASE_ORDER.join(", ")}`, "error");
474
474
  return;
475
475
  }
476
476
  requested = requestedText;
@@ -482,67 +482,67 @@ export default function (pi: ExtensionAPI) {
482
482
  });
483
483
 
484
484
  pi.registerCommand("kd-artifact", {
485
- description: "Create or update a phase artifact: /kd-artifact [phase] [content]",
485
+ description: "创建或更新阶段文档:/kd-artifact [阶段] [内容]",
486
486
  handler: async (args, ctx) => {
487
487
  const run = requireRun(ctx.cwd);
488
488
  if (!run) {
489
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
489
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
490
490
  return;
491
491
  }
492
492
 
493
493
  const parsed = parseArtifactArgs(args, run.phase);
494
494
  if (!parsed) {
495
- ctx.ui.notify("Usage: /kd-artifact [phase] [content]", "error");
495
+ ctx.ui.notify("用法:/kd-artifact [阶段] [内容]", "error");
496
496
  return;
497
497
  }
498
498
 
499
499
  const path = parsed.content
500
500
  ? updatePhaseArtifact(ctx.cwd, run, parsed.phase, parsed.content)
501
501
  : ensurePhaseArtifact(ctx.cwd, run, parsed.phase);
502
- ctx.ui.notify(`Artifact ready: ${path}`, "info");
502
+ ctx.ui.notify(`阶段文档已就绪:${path}`, "info");
503
503
  },
504
504
  });
505
505
 
506
506
  pi.registerCommand("kd-answer", {
507
- description: "Answer a blocking Kingdee harness question: /kd-answer Q-001 <answer>",
507
+ description: "回答一个阻塞中的 Kingdee Harness 问题:/kd-answer Q-001 <答案>",
508
508
  handler: async (args, ctx) => {
509
509
  const run = requireRun(ctx.cwd);
510
510
  if (!run) {
511
- ctx.ui.notify("No active Kingdee harness run. Use /kd-start <goal>.", "error");
511
+ ctx.ui.notify("当前没有 active Kingdee Harness run。请使用 /kd-start <需求>。", "error");
512
512
  return;
513
513
  }
514
514
  const [id, ...answerParts] = args.trim().split(/\s+/);
515
515
  const answer = answerParts.join(" ").trim();
516
516
  if (!id || !answer) {
517
- ctx.ui.notify("Usage: /kd-answer Q-001 <answer>", "error");
517
+ ctx.ui.notify("用法:/kd-answer Q-001 <答案>", "error");
518
518
  return;
519
519
  }
520
520
  const answered = answerQuestion(ctx.cwd, run, id, answer);
521
521
  if (!answered) {
522
- ctx.ui.notify(`Question not found: ${id}`, "error");
522
+ ctx.ui.notify(`未找到问题:${id}`, "error");
523
523
  return;
524
524
  }
525
- appendQuestionEventToArtifact(ctx.cwd, run, [`- Answered ${answered.id}: ${answered.answer}`]);
526
- ctx.ui.notify(`Recorded answer for ${answered.id}`, "info");
525
+ appendQuestionEventToArtifact(ctx.cwd, run, [`- 已回答 ${answered.id}:${answered.answer}`]);
526
+ ctx.ui.notify(`已记录 ${answered.id} 的答案`, "info");
527
527
  },
528
528
  });
529
529
  }
530
530
 
531
531
  function formatQuestions(run: NonNullable<ReturnType<typeof readActiveRun>>): string {
532
532
  const questions = run.questions ?? [];
533
- if (questions.length === 0) return "No harness questions recorded.";
533
+ if (questions.length === 0) return "尚未记录 Harness 问题。";
534
534
  return questions.map(formatQuestionCard).join("\n\n");
535
535
  }
536
536
 
537
537
  function formatRuns(runs: NonNullable<ReturnType<typeof readActiveRun>>[], activeId?: string): string {
538
- if (runs.length === 0) return "No Kingdee harness runs in this project. Start one with /kd-start <goal>.";
538
+ if (runs.length === 0) return "当前项目没有 Kingdee Harness run。请使用 /kd-start <需求> 创建。";
539
539
  return runs
540
540
  .map((run) =>
541
541
  [
542
542
  `${run.id === activeId ? "*" : "-"} ${run.id}`,
543
- ` Goal: ${run.goal ?? "unknown"}`,
544
- ` Status: ${run.status ?? "active"} | Phase: ${run.phase} | Product: ${run.profile?.product ?? run.product ?? "unknown"}`,
545
- ` Updated: ${run.updatedAt ?? "unknown"}`,
543
+ ` 需求:${run.goal ?? "未知"}`,
544
+ ` 状态:${run.status ?? "active"} | 阶段:${run.phase} | 产品:${run.profile?.product ?? run.product ?? "unknown"}`,
545
+ ` 更新时间:${run.updatedAt ?? "未知"}`,
546
546
  ].join("\n"),
547
547
  )
548
548
  .join("\n\n");
@@ -550,14 +550,14 @@ function formatRuns(runs: NonNullable<ReturnType<typeof readActiveRun>>[], activ
550
550
 
551
551
  function formatQuestionCard(question: NonNullable<NonNullable<ReturnType<typeof readActiveRun>>["questions"]>[number]): string {
552
552
  const lines = [
553
- `Question ${question.id} [${question.status}${question.blocking ? ", blocking" : ""}]`,
554
- `Phase: ${question.phase}`,
555
- `Question: ${question.question}`,
556
- question.reason ? `Reason: ${question.reason}` : undefined,
557
- question.choices?.length ? `Choices: ${question.choices.join(" | ")}` : undefined,
558
- question.answer ? `Answer: ${question.answer}` : undefined,
553
+ `问题 ${question.id} [${question.status}${question.blocking ? ", blocking" : ""}]`,
554
+ `阶段:${question.phase}`,
555
+ `问题:${question.question}`,
556
+ question.reason ? `原因:${question.reason}` : undefined,
557
+ question.choices?.length ? `选项:${question.choices.join(" | ")}` : undefined,
558
+ question.answer ? `答案:${question.answer}` : undefined,
559
559
  question.status === "open"
560
- ? `Answer the dialog if available, or reply in chat and record it using kd_question action=answer id=${question.id} answer=<answer>.`
560
+ ? `如果有弹窗,请直接回答;否则请在对话中回复,并用 kd_question action=answer id=${question.id} answer=<答案> 记录。`
561
561
  : undefined,
562
562
  ];
563
563
  return lines.filter(Boolean).join("\n");
@@ -589,12 +589,12 @@ async function askQuestionInteractively(
589
589
  function questionBatchProblem(question: string, choices?: string[]): string | undefined {
590
590
  const text = question.trim();
591
591
  const numberedItems = text.split(/\r?\n/).filter((line) => /^\s*(\d+[\.\)、)]|[-*]\s+)/.test(line)).length;
592
- if (numberedItems >= 2) return "kd_question rejected a batched checklist question.";
593
- if ((text.match(/[??]/g) ?? []).length > 1) return "kd_question rejected multiple questions in one prompt.";
594
- if (text.length > 220) return "kd_question rejected an overlong question. Ask only the smallest blocking question first.";
595
- if ((choices?.length ?? 0) > 3) return "kd_question rejected too many choices. Provide at most 3 concise choices.";
592
+ if (numberedItems >= 2) return "kd_question 拒绝了批量清单式问题。";
593
+ if ((text.match(/[??]/g) ?? []).length > 1) return "kd_question 拒绝了一次包含多个问题的提问。";
594
+ if (text.length > 220) return "kd_question 拒绝了过长问题。请只问当前最小的阻塞问题。";
595
+ if ((choices?.length ?? 0) > 3) return "kd_question 拒绝了过多选项。最多提供 3 个简短选项。";
596
596
  if (choices?.some((choice) => choice.length > 40 || /[\r\n??]/.test(choice))) {
597
- return "kd_question rejected complex choices. Choices must be short labels, not nested questions.";
597
+ return "kd_question 拒绝了复杂选项。选项必须是短标签,不能嵌套问题。";
598
598
  }
599
599
  return undefined;
600
600
  }
@@ -602,14 +602,14 @@ function questionBatchProblem(question: string, choices?: string[]): string | un
602
602
  function formatQuestionArtifactLines(question: NonNullable<NonNullable<ReturnType<typeof readActiveRun>>["questions"]>[number]): string[] {
603
603
  return [
604
604
  `- ${question.id} [${question.blocking ? "blocking" : "non-blocking"}] ${question.question}`,
605
- question.reason ? ` - Reason: ${question.reason}` : undefined,
606
- question.choices?.length ? ` - Choices: ${question.choices.join(" | ")}` : undefined,
607
- " - Status: open",
605
+ question.reason ? ` - 原因:${question.reason}` : undefined,
606
+ question.choices?.length ? ` - 选项:${question.choices.join(" | ")}` : undefined,
607
+ " - 状态:open",
608
608
  ].filter(Boolean) as string[];
609
609
  }
610
610
 
611
611
  function appendQuestionEventToArtifact(cwd: string, run: NonNullable<ReturnType<typeof readActiveRun>>, lines: string[]): void {
612
612
  const existing = readArtifact(cwd, run, run.phase) ?? "";
613
- const section = ["", "## Harness Questions", "", ...lines, ""].join("\n");
614
- updatePhaseArtifact(cwd, run, run.phase, existing.includes("## Harness Questions") ? `${existing.trimEnd()}\n${lines.join("\n")}\n` : `${existing.trimEnd()}${section}`);
613
+ const section = ["", "## Harness 问题", "", ...lines, ""].join("\n");
614
+ updatePhaseArtifact(cwd, run, run.phase, existing.includes("## Harness 问题") ? `${existing.trimEnd()}\n${lines.join("\n")}\n` : `${existing.trimEnd()}${section}`);
615
615
  }
@@ -32,7 +32,7 @@ function readActiveRun(cwd: string): ActiveRun | undefined {
32
32
  }
33
33
 
34
34
  function formatProduct(run: ActiveRun | undefined): string {
35
- if (!run) return "unselected";
35
+ if (!run) return "未选择";
36
36
  const product = run.profile?.product ?? run.product ?? "unknown";
37
37
  const techStack = run.profile?.techStack ?? "unknown";
38
38
  const language = run.profile?.language ?? "unknown";
@@ -40,13 +40,13 @@ function formatProduct(run: ActiveRun | undefined): string {
40
40
  }
41
41
 
42
42
  function formatPhase(phase: ActiveRun["phase"]): string {
43
- return phase ?? "idle";
43
+ return phase ?? "空闲";
44
44
  }
45
45
 
46
46
  function formatGate(run: ActiveRun | undefined): string {
47
- if (!run?.gate) return "gate: pending";
48
- if (run.gate.passed) return "gate: pass";
49
- return `gate: blocked${run.gate.reason ? ` - ${run.gate.reason}` : ""}`;
47
+ if (!run?.gate) return "门禁:待检查";
48
+ if (run.gate.passed) return "门禁:通过";
49
+ return `门禁:阻塞${run.gate.reason ? ` - ${run.gate.reason}` : ""}`;
50
50
  }
51
51
 
52
52
  function padOrTrim(text: string, width: number): string {
@@ -60,7 +60,7 @@ function logoLines(theme: Theme): string[] {
60
60
  const muted = (text: string) => theme.fg("muted", text);
61
61
 
62
62
  return [
63
- `${accent("KCode")} ${muted("Kingdee Pi Harness")}`,
63
+ `${accent("KCode")} ${muted("金蝶 Pi Harness")}`,
64
64
  `${accent("=====")} ${muted("discuss -> spec -> plan -> execute -> verify -> ship")}`,
65
65
  ];
66
66
  }
@@ -76,15 +76,15 @@ export default function (pi: ExtensionAPI) {
76
76
  const phase = formatPhase(run?.phase);
77
77
  const product = formatProduct(run);
78
78
  const gate = formatGate(run);
79
- const risk = run?.risk ?? "unknown";
80
- const runId = run?.id ?? "none";
79
+ const risk = run?.risk ?? "未知";
80
+ const runId = run?.id ?? "";
81
81
 
82
82
  const status = [
83
- theme.fg("muted", "phase: "),
83
+ theme.fg("muted", "阶段:"),
84
84
  theme.fg("accent", phase),
85
- theme.fg("muted", " | product: "),
85
+ theme.fg("muted", " | 产品:"),
86
86
  theme.fg("text", product),
87
- theme.fg("muted", " | risk: "),
87
+ theme.fg("muted", " | 风险:"),
88
88
  theme.fg(risk === "high" ? "error" : risk === "medium" ? "warning" : "success", risk),
89
89
  theme.fg("muted", " | "),
90
90
  theme.fg(run?.gate?.passed === false ? "error" : "muted", gate),
@@ -94,7 +94,7 @@ export default function (pi: ExtensionAPI) {
94
94
  "",
95
95
  ...logoLines(theme).map((line) => padOrTrim(line, width)),
96
96
  padOrTrim(status, width),
97
- padOrTrim(theme.fg("dim", `run: ${runId}`), width),
97
+ padOrTrim(theme.fg("dim", `run:${runId}`), width),
98
98
  "",
99
99
  ];
100
100
  },
@@ -104,19 +104,19 @@ export default function (pi: ExtensionAPI) {
104
104
  });
105
105
 
106
106
  pi.registerCommand("kcode-header", {
107
- description: "Restore the KCode Kingdee header",
107
+ description: "恢复 KCode 金蝶标题栏",
108
108
  handler: async (_args, ctx) => {
109
109
  if (ctx.mode !== "tui") return;
110
- ctx.ui.notify("Restart the session or reload extensions to restore the KCode header.", "info");
110
+ ctx.ui.notify("请重启会话或重新加载扩展以恢复 KCode 标题栏。", "info");
111
111
  },
112
112
  });
113
113
 
114
114
  pi.registerCommand("builtin-header", {
115
- description: "Restore Pi's built-in header",
115
+ description: "恢复 Pi 内置标题栏",
116
116
  handler: async (_args, ctx) => {
117
117
  if (ctx.mode !== "tui") return;
118
118
  ctx.ui.setHeader(undefined);
119
- ctx.ui.notify("Built-in header restored", "info");
119
+ ctx.ui.notify("已恢复 Pi 内置标题栏", "info");
120
120
  },
121
121
  });
122
122
  }