zapmyco 0.7.0 → 0.8.0

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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { B as APP_NAME, E as createLlmBasedAgent, F as eventBus, H as __VERSION__, I as buildSkillSnapshot, L as loadSkills, S as WebError, U as __require, V as SESSION_DIR_NAME, g as logger, h as configureLogger, i as AgentLlmFacade, n as loadConfig, p as SubAgentManager, t as HOME_CONFIG_PATH, w as ZapmycoErrorCode } from "../loader-B5_elj6d.mjs";
2
+ import { B as APP_NAME, E as createLlmBasedAgent, F as eventBus, H as VERSION, I as buildSkillSnapshot, L as loadSkills, S as WebError, U as __require, V as SESSION_DIR_NAME, g as logger, h as configureLogger, i as AgentLlmFacade, n as loadConfig, p as SubAgentManager, t as HOME_CONFIG_PATH, w as ZapmycoErrorCode } from "../loader-C_55Y7Q_.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
@@ -13,6 +13,7 @@ import chalk, { Chalk } from "chalk";
13
13
  import { Command } from "commander";
14
14
  import { spawn, spawnSync } from "node:child_process";
15
15
  import { CombinedAutocompleteProvider, Container, Editor, Input, Key, ProcessTerminal, SelectList, TUI, getKeybindings, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
16
+ import i18next from "i18next";
16
17
  import TurndownService from "turndown";
17
18
  import { lookup } from "node:dns/promises";
18
19
  import { Client } from "@modelcontextprotocol/sdk/client";
@@ -365,6 +366,391 @@ function createQuitCommand() {
365
366
  };
366
367
  }
367
368
 
369
+ //#endregion
370
+ //#region src/i18n/locales/en.json
371
+ var en_default = {
372
+ output: {
373
+ "welcome": "Welcome back!",
374
+ "error": {
375
+ "executionFailed": "Execution failed:",
376
+ "detail": "Details:"
377
+ },
378
+ "result": {
379
+ "title": "Execution Complete",
380
+ "goal": "Goal:",
381
+ "status": "Status:",
382
+ "success": "Success",
383
+ "partialSuccess": "Partial Success",
384
+ "failed": "Failed",
385
+ "duration": "Duration:",
386
+ "token": "Token:",
387
+ "cost": "Cost:",
388
+ "taskBreakdown": "Task Breakdown",
389
+ "subtaskCount": "subtasks",
390
+ "artifacts": "Artifacts:",
391
+ "suggestions": "Suggestions:"
392
+ },
393
+ "taskGraph": {
394
+ "title": "Task Breakdown Overview",
395
+ "total": "{{count}} subtasks, {{layers}} parallel layers",
396
+ "layer": "Layer {{index}} (parallel):"
397
+ },
398
+ "agents": {
399
+ "title": "Registered Agents",
400
+ "empty": "No registered agents",
401
+ "id": "ID",
402
+ "status": "Status",
403
+ "load": "Load",
404
+ "capability": "Capabilities"
405
+ },
406
+ "config": {
407
+ "title": "Current Configuration",
408
+ "llm": "LLM:",
409
+ "defaultModel": "Default Model:",
410
+ "provider": "Provider:",
411
+ "modelId": "Model ID:",
412
+ "inputType": "Input Type:",
413
+ "apiKey": "API Key:",
414
+ "apiKeyConfigured": "***Configured***",
415
+ "apiKeyNotConfigured": "(Not configured)",
416
+ "apiFormat": "API Format:",
417
+ "scheduler": "Scheduler:",
418
+ "maxConcurrency": "Max Concurrency:",
419
+ "maxPerAgent": "Max Per Agent:",
420
+ "taskTimeout": "Task Timeout:",
421
+ "maxRetries": "Max Retries:",
422
+ "cli": "CLI:",
423
+ "colorEnabled": "Enabled",
424
+ "colorDisabled": "Disabled",
425
+ "debugEnabled": "Enabled",
426
+ "debugDisabled": "Disabled",
427
+ "outputFormat": "Output Format:",
428
+ "uiLanguage": "UI Language:",
429
+ "agents": "Agents:",
430
+ "minutes": "min"
431
+ },
432
+ "history": {
433
+ "title": "Session History",
434
+ "empty": "No history yet"
435
+ },
436
+ "status": {
437
+ "title": "Session Status",
438
+ "idle": "Idle",
439
+ "executing": "Executing",
440
+ "closing": "Closing",
441
+ "state": "State:",
442
+ "totalRequests": "Total Requests:",
443
+ "success": "Success:",
444
+ "failure": "Failure:",
445
+ "tokenConsumption": "Token Usage:",
446
+ "totalCost": "Total Cost:"
447
+ }
448
+ },
449
+ dialog: {
450
+ "footer": {
451
+ "search": "Type to search · ↑↓ Navigate · Enter Confirm · Esc Cancel",
452
+ "normal": "k/j ↑↓ Navigate · / Search · Enter Select · Esc/q Quit · BS/h Back",
453
+ "searchNarrow": "Enter Confirm · Esc Cancel",
454
+ "normalNarrow": "↑↓=k/j / Enter Esc/q BS/h",
455
+ "textInput": "Enter to confirm · Esc to cancel"
456
+ },
457
+ "configView": {
458
+ "narrow": "q/Esc Back",
459
+ "wide": "q/Esc Back · Enter/BS/h Back to Menu"
460
+ }
461
+ },
462
+ settings: {
463
+ "mainMenu": {
464
+ "defaultModel": "Default Model",
465
+ "manageProviders": "Manage Providers",
466
+ "viewConfig": "View Config",
467
+ "language": "Language / 语言",
468
+ "notConfigured": "not configured",
469
+ "nConfigured": "{{count}} configured",
470
+ "noneConfigured": "none configured",
471
+ "displayFullConfig": "Display full configuration details"
472
+ },
473
+ "apiKeyConfig": {
474
+ "useEnvVar": "Use env var",
475
+ "useEnvVarDesc": "Recommended — more secure",
476
+ "enterManually": "Enter manually",
477
+ "enterManuallyDesc": "Type the key directly (stored in plaintext in settings.json)",
478
+ "clear": "Clear",
479
+ "clearDesc": "Remove the configured key"
480
+ },
481
+ "providerActions": {
482
+ "configureApiKey": "Configure API Key",
483
+ "selectModel": "Select Model",
484
+ "baseUrl": "Base URL",
485
+ "setAsDefault": "Set as Default"
486
+ },
487
+ "providerEntry": {
488
+ "keyConfigured": "key configured",
489
+ "noKey": "no key",
490
+ "addProvider": "Add Provider",
491
+ "addProviderDesc": "Select from a list of known providers",
492
+ "apiFormat": "API format: {{format}}",
493
+ "openaiCompatible": "OpenAI compatible"
494
+ },
495
+ "addProvider": {
496
+ "yes": "Yes, configure API Key now",
497
+ "yesDesc": "Go to API Key settings",
498
+ "later": "Later",
499
+ "laterDesc": "Return to main menu"
500
+ },
501
+ "messages": {
502
+ "apiKeyCleared": "[ok] Cleared API key for {{provider}}",
503
+ "apiKeySetToEnv": "[ok] Set {{provider}} API key to env var ${ {{envVar}} }",
504
+ "envVarNote": "Make sure {{envVar}} is set in your shell",
505
+ "apiKeyConfigured": "[ok] Configured API key for {{provider}}",
506
+ "modelSelected": "[ok] Selected model: {{model}}",
507
+ "providerAdded": "[ok] Added provider: {{provider}}",
508
+ "baseUrlReset": "[ok] Reset {{provider}} Base URL to default",
509
+ "baseUrlSet": "[ok] Set {{provider}} Base URL: {{url}}",
510
+ "defaultModelSet": "[ok] Default model set to: {{model}}",
511
+ "languageSet": "[ok] Language set to: {{locale}}",
512
+ "restartRequired": "Some changes may require a session restart to take full effect.",
513
+ "noModels": "has no available model list",
514
+ "configureManually": "Configure models manually in settings.json or check the provider name",
515
+ "alreadyExists": "already exists, use the provider entry to configure it",
516
+ "configureFirst": "Configure a model first before setting it as default",
517
+ "checkApiKeyHelpPrefix": " Hint: check"
518
+ },
519
+ "cliMode": {
520
+ "usage": "Usage:",
521
+ "settingsUsage": "/settings — Open interactive configuration menu",
522
+ "listProvidersUsage": "/settings list-providers — List all known providers",
523
+ "listModelsUsage": "/settings list-models <name> — List available models for a provider",
524
+ "knownProviders": "Known providers:",
525
+ "noModelsAvailable": "No models available from pi-ai registry",
526
+ "hintConfigureFirst": "Hint: use /settings to configure this provider first",
527
+ "availableModels": "available models:"
528
+ },
529
+ "modelSelector": { "notConfigured": "Not configured - Enter to set API Key" }
530
+ },
531
+ session: {
532
+ "welcome": "ZapMyco: Welcome back!",
533
+ "errorPrefix": "[Error]",
534
+ "agentErrorMessage": "Agent execution failed (no detailed error information)",
535
+ "noContentError": "Model returned no content, please check your API Key configuration",
536
+ "displayName": "Zapmyco AI Assistant",
537
+ "editorNotFound": "Editor not found: {{cmd}}, please set $EDITOR environment variable",
538
+ "editorFailed": "Failed to open editor: {{message}}",
539
+ "executing": "Executing...",
540
+ "setEnvVarHint": "Set environment variable: export {{envVar}}=<your-api-key>",
541
+ "useConfigHint": "Or use in REPL: /config set llm.providers.{{provider}}.apiKey <your-key>"
542
+ }
543
+ };
544
+
545
+ //#endregion
546
+ //#region src/i18n/locales/zh-CN.json
547
+ var zh_CN_default = {
548
+ output: {
549
+ "welcome": "欢迎回来!",
550
+ "error": {
551
+ "executionFailed": "执行失败:",
552
+ "detail": "详情:"
553
+ },
554
+ "result": {
555
+ "title": "执行完成",
556
+ "goal": "目标:",
557
+ "status": "状态:",
558
+ "success": "成功",
559
+ "partialSuccess": "部分成功",
560
+ "failed": "失败",
561
+ "duration": "耗时:",
562
+ "token": "Token:",
563
+ "cost": "成本:",
564
+ "taskBreakdown": "任务拆分",
565
+ "subtaskCount": "个子任务",
566
+ "artifacts": "制品:",
567
+ "suggestions": "建议:"
568
+ },
569
+ "taskGraph": {
570
+ "title": "任务拆分概览",
571
+ "total": "共 {{count}} 个子任务,{{layers}} 层并行",
572
+ "layer": "第 {{index}} 层 (可并行):"
573
+ },
574
+ "agents": {
575
+ "title": "已注册 Agent",
576
+ "empty": "暂无已注册的 Agent",
577
+ "id": "ID",
578
+ "status": "状态",
579
+ "load": "负载",
580
+ "capability": "能力"
581
+ },
582
+ "config": {
583
+ "title": "当前配置",
584
+ "llm": "LLM:",
585
+ "defaultModel": "默认模型:",
586
+ "provider": "提供商:",
587
+ "modelId": "模型 ID:",
588
+ "inputType": "输入类型:",
589
+ "apiKey": "API Key:",
590
+ "apiKeyConfigured": "***已配置***",
591
+ "apiKeyNotConfigured": "(未配置)",
592
+ "apiFormat": "API 格式:",
593
+ "scheduler": "调度器:",
594
+ "maxConcurrency": "最大并行:",
595
+ "maxPerAgent": "单 Agent 最大并发:",
596
+ "taskTimeout": "任务超时:",
597
+ "maxRetries": "最大重试:",
598
+ "cli": "CLI:",
599
+ "colorEnabled": "开启",
600
+ "colorDisabled": "关闭",
601
+ "debugEnabled": "开启",
602
+ "debugDisabled": "关闭",
603
+ "outputFormat": "输出格式:",
604
+ "uiLanguage": "UI 语言:",
605
+ "agents": "Agents:",
606
+ "minutes": "分钟"
607
+ },
608
+ "history": {
609
+ "title": "会话历史",
610
+ "empty": "暂无历史记录"
611
+ },
612
+ "status": {
613
+ "title": "会话状态",
614
+ "idle": "空闲",
615
+ "executing": "执行中",
616
+ "closing": "关闭中",
617
+ "state": "状态:",
618
+ "totalRequests": "总请求数:",
619
+ "success": "成功:",
620
+ "failure": "失败:",
621
+ "tokenConsumption": "Token 消耗:",
622
+ "totalCost": "总成本:"
623
+ }
624
+ },
625
+ dialog: {
626
+ "footer": {
627
+ "search": "输入文字搜索 · ↑↓ 导航 · Enter 确认 · Esc 取消",
628
+ "normal": "k/j ↑↓ 导航 · / 搜索 · Enter 选择 · Esc/q 退出 · BS/h 返回",
629
+ "searchNarrow": "Enter 确认 · Esc 取消",
630
+ "normalNarrow": "↑↓=k/j / Enter Esc/q BS/h",
631
+ "textInput": "Enter to confirm · Esc to cancel"
632
+ },
633
+ "configView": {
634
+ "narrow": "q/Esc 返回",
635
+ "wide": "q/Esc 返回 · Enter/BS/h 返回菜单"
636
+ }
637
+ },
638
+ settings: {
639
+ "mainMenu": {
640
+ "defaultModel": "默认模型",
641
+ "manageProviders": "管理提供商",
642
+ "viewConfig": "查看配置",
643
+ "language": "Language / 语言",
644
+ "notConfigured": "未配置",
645
+ "nConfigured": "已配置 {{count}} 个",
646
+ "noneConfigured": "未配置",
647
+ "displayFullConfig": "显示完整配置详情"
648
+ },
649
+ "apiKeyConfig": {
650
+ "useEnvVar": "使用环境变量",
651
+ "useEnvVarDesc": "推荐 — 更安全",
652
+ "enterManually": "手动输入",
653
+ "enterManuallyDesc": "直接输入密钥(以明文存储在 settings.json 中)",
654
+ "clear": "清除",
655
+ "clearDesc": "移除已配置的密钥"
656
+ },
657
+ "providerActions": {
658
+ "configureApiKey": "配置 API Key",
659
+ "selectModel": "选择模型",
660
+ "baseUrl": "Base URL",
661
+ "setAsDefault": "设为默认"
662
+ },
663
+ "providerEntry": {
664
+ "keyConfigured": "已配置密钥",
665
+ "noKey": "无密钥",
666
+ "addProvider": "添加提供商",
667
+ "addProviderDesc": "从已知提供商列表中选择",
668
+ "apiFormat": "API 格式: {{format}}",
669
+ "openaiCompatible": "OpenAI 兼容"
670
+ },
671
+ "addProvider": {
672
+ "yes": "是,立即配置 API Key",
673
+ "yesDesc": "前往 API Key 设置",
674
+ "later": "稍后",
675
+ "laterDesc": "返回主菜单"
676
+ },
677
+ "messages": {
678
+ "apiKeyCleared": "[ok] 已清除 {{provider}} 的 API Key",
679
+ "apiKeySetToEnv": "[ok] {{provider}} 的 API Key 已设置为环境变量 ${ {{envVar}} }",
680
+ "envVarNote": "请确保 {{envVar}} 已在 shell 中设置",
681
+ "apiKeyConfigured": "[ok] 已配置 {{provider}} 的 API Key",
682
+ "modelSelected": "[ok] 已选择模型: {{model}}",
683
+ "providerAdded": "[ok] 已添加提供商: {{provider}}",
684
+ "baseUrlReset": "[ok] 已重置 {{provider}} 的 Base URL 为默认值",
685
+ "baseUrlSet": "[ok] 已设置 {{provider}} 的 Base URL: {{url}}",
686
+ "defaultModelSet": "[ok] 默认模型已设置为: {{model}}",
687
+ "languageSet": "[ok] 语言已设置为: {{locale}}",
688
+ "restartRequired": "部分更改可能需要重启会话才能完全生效。",
689
+ "noModels": "没有可用的模型列表",
690
+ "configureManually": "请在 settings.json 中手动配置模型或检查提供商名称",
691
+ "alreadyExists": "已存在,请使用该提供商条目进行配置",
692
+ "configureFirst": "请先配置模型再设为默认",
693
+ "checkApiKeyHelpPrefix": " 提示: 请检查"
694
+ },
695
+ "cliMode": {
696
+ "usage": "用法:",
697
+ "settingsUsage": "/settings — 打开交互式配置菜单",
698
+ "listProvidersUsage": "/settings list-providers — 列出所有已知提供商",
699
+ "listModelsUsage": "/settings list-models <name> — 列出提供商可用的模型",
700
+ "knownProviders": "已知提供商:",
701
+ "noModelsAvailable": "pi-ai 注册表中没有可用的模型",
702
+ "hintConfigureFirst": "提示: 使用 /settings 先配置此提供商",
703
+ "availableModels": "可用模型:"
704
+ },
705
+ "modelSelector": { "notConfigured": "未配置 - Enter 设置 API Key" }
706
+ },
707
+ session: {
708
+ "welcome": "ZapMyco: 欢迎回来!",
709
+ "errorPrefix": "[错误]",
710
+ "agentErrorMessage": "Agent 执行失败(无详细错误信息)",
711
+ "noContentError": "模型未返回任何内容,请检查 API Key 配置",
712
+ "displayName": "Zapmyco AI 助手",
713
+ "editorNotFound": "未找到编辑器: {{cmd}},请设置 $EDITOR 环境变量",
714
+ "editorFailed": "打开编辑器失败: {{message}}",
715
+ "executing": "正在执行...",
716
+ "setEnvVarHint": "请设置环境变量: export {{envVar}}=<your-api-key>",
717
+ "useConfigHint": "或在 REPL 中使用: /config set llm.providers.{{provider}}.apiKey <your-key>"
718
+ }
719
+ };
720
+
721
+ //#endregion
722
+ //#region src/i18n/index.ts
723
+ /**
724
+ * i18next 国际化模块
725
+ *
726
+ * 使用 i18next 实现国际化支持。
727
+ * 通过传入静态 resources 实现同步初始化,无需 async/await。
728
+ * 翻译文件为标准 JSON 格式,未来 React Web 界面可复用。
729
+ */
730
+ i18next.init({
731
+ lng: "zh-CN",
732
+ fallbackLng: "zh-CN",
733
+ resources: {
734
+ "zh-CN": { translation: zh_CN_default },
735
+ en: { translation: en_default }
736
+ },
737
+ initAsync: false,
738
+ interpolation: {
739
+ escapeValue: false,
740
+ prefix: "{{",
741
+ suffix: "}}"
742
+ },
743
+ returnNull: false,
744
+ returnEmptyString: true
745
+ });
746
+ const { t } = i18next;
747
+ /**
748
+ * 设置当前语言
749
+ */
750
+ function setLocale(locale) {
751
+ i18next.changeLanguage(locale).catch(() => {});
752
+ }
753
+
368
754
  //#endregion
369
755
  //#region src/cli/repl/components/dialogs.ts
370
756
  /**
@@ -479,10 +865,10 @@ var SelectListWithFooter = class {
479
865
  for (let i = 0; i < padding; i++) lines.push("");
480
866
  if (width >= 50) {
481
867
  lines.push(chalk.gray(` ${"─".repeat(Math.max(0, width - 4))}`));
482
- if (this.isFiltering) lines.push(chalk.gray(" 输入文字搜索 · ↑↓ 导航 · Enter 确认 · Esc 取消"));
483
- else lines.push(chalk.gray(" k/j ↑↓ 导航 · / 搜索 · Enter 选择 · Esc/q 退出 · BS/h 返回"));
484
- } else if (this.isFiltering) lines.push(chalk.gray(" Enter 确认 · Esc 取消"));
485
- else lines.push(chalk.gray(" ↑↓=k/j / Enter Esc/q BS/h"));
868
+ if (this.isFiltering) lines.push(chalk.gray(` ${t("dialog.footer.search")}`));
869
+ else lines.push(chalk.gray(` ${t("dialog.footer.normal")}`));
870
+ } else if (this.isFiltering) lines.push(chalk.gray(` ${t("dialog.footer.searchNarrow")}`));
871
+ else lines.push(chalk.gray(` ${t("dialog.footer.normalNarrow")}`));
486
872
  lines.push("");
487
873
  return lines;
488
874
  }
@@ -551,7 +937,7 @@ var TextInputComponent = class {
551
937
  ` ${this.input.render(width - 4)[0] ?? ""}`,
552
938
  c.gray(` ${"─".repeat(Math.min(width - 4, 50))}`),
553
939
  "",
554
- c.gray(" Enter to confirm · Esc to cancel"),
940
+ c.gray(` ${t("dialog.footer.textInput")}`),
555
941
  ""
556
942
  ];
557
943
  }
@@ -600,13 +986,13 @@ var ConfigViewComponent = class {
600
986
  if (width < 50) return [
601
987
  ...this.lines,
602
988
  "",
603
- chalk.gray(" q/Esc 返回")
989
+ chalk.gray(` ${t("dialog.configView.narrow")}`)
604
990
  ];
605
991
  return [
606
992
  ...this.lines,
607
993
  "",
608
994
  chalk.gray(` ${"─".repeat(Math.max(0, width - 4))}`),
609
- chalk.gray(" q/Esc 返回 · Enter/BS/h 返回菜单"),
995
+ chalk.gray(` ${t("dialog.configView.wide")}`),
610
996
  ""
611
997
  ];
612
998
  }
@@ -766,6 +1152,16 @@ const KNOWN_PROVIDERS = [
766
1152
  label: "OpenCode"
767
1153
  }
768
1154
  ];
1155
+ /** Supported language locales */
1156
+ const SUPPORTED_LOCALES = [{
1157
+ value: "zh-CN",
1158
+ label: "简体中文",
1159
+ description: "Chinese (Simplified)"
1160
+ }, {
1161
+ value: "en",
1162
+ label: "English",
1163
+ description: "English"
1164
+ }];
769
1165
  /** Set a dot-path value, persist to disk and hot-reload agent */
770
1166
  function setConfigValue(session, dotPath, value) {
771
1167
  const settings = readSettings();
@@ -819,7 +1215,7 @@ async function handleCommandLine(args, session, _tui, config) {
819
1215
  case "list-providers": {
820
1216
  const providers = _getByDotPath(config, "llm.providers");
821
1217
  const names = providers ? Object.keys(providers) : [];
822
- const lines = ["", "Known providers:"];
1218
+ const lines = ["", t("settings.cliMode.knownProviders")];
823
1219
  for (const p of KNOWN_PROVIDERS) {
824
1220
  const configured = names.includes(p.id) ? " ✓" : " ";
825
1221
  lines.push(` ${configured} ${p.label} (${p.id})`);
@@ -831,7 +1227,7 @@ async function handleCommandLine(args, session, _tui, config) {
831
1227
  if (!args[1]) {
832
1228
  session.appendOutput([
833
1229
  "",
834
- "Usage: /settings list-models <provider>",
1230
+ `${t("settings.cliMode.usage")} /settings list-models <provider>`,
835
1231
  ""
836
1232
  ]);
837
1233
  return;
@@ -840,15 +1236,15 @@ async function handleCommandLine(args, session, _tui, config) {
840
1236
  if (modelIds.length === 0) {
841
1237
  session.appendOutput([
842
1238
  "",
843
- `Provider "${args[1]}" has no known models`,
844
- "Hint: use /settings to configure this provider first",
1239
+ `Provider "${args[1]}" ${t("settings.messages.noModels")}`,
1240
+ t("settings.cliMode.hintConfigureFirst"),
845
1241
  ""
846
1242
  ]);
847
1243
  return;
848
1244
  }
849
1245
  session.appendOutput([
850
1246
  "",
851
- `${args[1]} available models:`,
1247
+ `${args[1]} ${t("settings.cliMode.availableModels")}`,
852
1248
  ...modelIds.map((id) => ` - ${id}`),
853
1249
  ""
854
1250
  ]);
@@ -856,10 +1252,10 @@ async function handleCommandLine(args, session, _tui, config) {
856
1252
  }
857
1253
  default: session.appendOutput([
858
1254
  "",
859
- "Usage:",
860
- " /settings — Open interactive configuration menu",
861
- " /settings list-providers — List all known providers",
862
- " /settings list-models <name> — List available models for a provider",
1255
+ t("settings.cliMode.usage"),
1256
+ ` ${t("settings.cliMode.settingsUsage")}`,
1257
+ ` ${t("settings.cliMode.listProvidersUsage")}`,
1258
+ ` ${t("settings.cliMode.listModelsUsage")}`,
863
1259
  ""
864
1260
  ]);
865
1261
  }
@@ -877,18 +1273,18 @@ async function handleInteractiveMode(tui, session, config) {
877
1273
  const choice = await showSelectList(tui, [
878
1274
  {
879
1275
  value: "env",
880
- label: `Use env var ${"${" + envVarName + "}"}`,
881
- description: "Recommended — more secure"
1276
+ label: `${t("settings.apiKeyConfig.useEnvVar")} \${${envVarName}}`,
1277
+ description: t("settings.apiKeyConfig.useEnvVarDesc")
882
1278
  },
883
1279
  {
884
1280
  value: "manual",
885
- label: "Enter manually",
886
- description: "Type the key directly (stored in plaintext in settings.json)"
1281
+ label: t("settings.apiKeyConfig.enterManually"),
1282
+ description: t("settings.apiKeyConfig.enterManuallyDesc")
887
1283
  },
888
1284
  {
889
1285
  value: "clear",
890
- label: "Clear",
891
- description: "Remove the configured key"
1286
+ label: t("settings.apiKeyConfig.clear"),
1287
+ description: t("settings.apiKeyConfig.clearDesc")
892
1288
  }
893
1289
  ], { onExit: exitAll });
894
1290
  if (!choice) return;
@@ -896,15 +1292,18 @@ async function handleInteractiveMode(tui, session, config) {
896
1292
  setConfigValue(session, `llm.providers.${providerName}.apiKey`, "");
897
1293
  session.appendOutput([
898
1294
  "",
899
- ` [ok] Cleared API key for ${providerName}`,
1295
+ ` ${t("settings.messages.apiKeyCleared", { provider: providerName })}`,
900
1296
  ""
901
1297
  ]);
902
1298
  } else if (choice.value === "env") {
903
1299
  setConfigValue(session, `llm.providers.${providerName}.apiKey`, `\${${envVarName}}`);
904
1300
  session.appendOutput([
905
1301
  "",
906
- ` [ok] Set ${providerName} API key to env var \${${envVarName}}`,
907
- ` Make sure ${envVarName} is set in your shell`,
1302
+ ` ${t("settings.messages.apiKeySetToEnv", {
1303
+ provider: providerName,
1304
+ envVar: envVarName
1305
+ })}`,
1306
+ ` ${t("settings.messages.envVarNote", { envVar: envVarName })}`,
908
1307
  ""
909
1308
  ]);
910
1309
  } else if (choice.value === "manual") {
@@ -913,7 +1312,7 @@ async function handleInteractiveMode(tui, session, config) {
913
1312
  setConfigValue(session, `llm.providers.${providerName}.apiKey`, key);
914
1313
  session.appendOutput([
915
1314
  "",
916
- ` [ok] Configured API key for ${providerName}`,
1315
+ ` ${t("settings.messages.apiKeyConfigured", { provider: providerName })}`,
917
1316
  ""
918
1317
  ]);
919
1318
  }
@@ -934,14 +1333,17 @@ async function handleInteractiveMode(tui, session, config) {
934
1333
  writeSettings(settings);
935
1334
  session.appendOutput([
936
1335
  "",
937
- ` [ok] Reset ${providerName} Base URL to default`,
1336
+ ` ${t("settings.messages.baseUrlReset", { provider: providerName })}`,
938
1337
  ""
939
1338
  ]);
940
1339
  } else {
941
1340
  setConfigValue(session, configPath, url);
942
1341
  session.appendOutput([
943
1342
  "",
944
- ` [ok] Set ${providerName} Base URL: ${url}`,
1343
+ ` ${t("settings.messages.baseUrlSet", {
1344
+ provider: providerName,
1345
+ url
1346
+ })}`,
945
1347
  ""
946
1348
  ]);
947
1349
  }
@@ -955,8 +1357,8 @@ async function handleInteractiveMode(tui, session, config) {
955
1357
  if (modelIds.length === 0) {
956
1358
  session.appendOutput([
957
1359
  "",
958
- ` ${providerName} has no available model list`,
959
- " Configure models manually in settings.json or check the provider name",
1360
+ ` ${providerName} ${t("settings.messages.noModels")}`,
1361
+ ` ${t("settings.messages.configureManually")}`,
960
1362
  ""
961
1363
  ]);
962
1364
  return;
@@ -974,7 +1376,7 @@ async function handleInteractiveMode(tui, session, config) {
974
1376
  _setByDotPath(session.config, `llm.providers.${providerName}.models`, _getByDotPath(settings, `llm.providers.${providerName}.models`) ?? {});
975
1377
  session.appendOutput([
976
1378
  "",
977
- ` [ok] Selected model: ${providerName}/${modelId}`,
1379
+ ` ${t("settings.messages.modelSelected", { model: `${providerName}/${modelId}` })}`,
978
1380
  ""
979
1381
  ]);
980
1382
  }
@@ -987,7 +1389,7 @@ async function handleInteractiveMode(tui, session, config) {
987
1389
  if (modelIds.length === 0) {
988
1390
  session.appendOutput([
989
1391
  "",
990
- " Configure a model first before setting it as default",
1392
+ ` ${t("settings.messages.configureFirst")}`,
991
1393
  ""
992
1394
  ]);
993
1395
  return;
@@ -996,7 +1398,7 @@ async function handleInteractiveMode(tui, session, config) {
996
1398
  setConfigValue(session, "llm.defaultModel", modelKey);
997
1399
  session.appendOutput([
998
1400
  "",
999
- ` [ok] Default model set to: ${modelKey}`,
1401
+ ` ${t("settings.messages.defaultModelSet", { model: modelKey })}`,
1000
1402
  ""
1001
1403
  ]);
1002
1404
  };
@@ -1011,18 +1413,23 @@ async function handleInteractiveMode(tui, session, config) {
1011
1413
  const choice = await showSelectList(tui, [
1012
1414
  {
1013
1415
  value: "default-model",
1014
- label: "Default Model",
1015
- description: String(_getByDotPath(state.current, "llm.defaultModel") ?? "not configured")
1416
+ label: t("settings.mainMenu.defaultModel"),
1417
+ description: String(_getByDotPath(state.current, "llm.defaultModel") ?? t("settings.mainMenu.notConfigured"))
1016
1418
  },
1017
1419
  {
1018
1420
  value: "manage-providers",
1019
- label: "Manage Providers",
1020
- description: providerCount > 0 ? `${providerCount} configured` : "none configured"
1421
+ label: t("settings.mainMenu.manageProviders"),
1422
+ description: providerCount > 0 ? t("settings.mainMenu.nConfigured", { count: providerCount }) : t("settings.mainMenu.noneConfigured")
1021
1423
  },
1022
1424
  {
1023
1425
  value: "view-config",
1024
- label: "View Config",
1025
- description: "Display full configuration details"
1426
+ label: t("settings.mainMenu.viewConfig"),
1427
+ description: t("settings.mainMenu.displayFullConfig")
1428
+ },
1429
+ {
1430
+ value: "language",
1431
+ label: t("settings.mainMenu.language"),
1432
+ description: String(_getByDotPath(state.current, "locale") ?? "zh-CN")
1026
1433
  }
1027
1434
  ], { onExit: exitAll });
1028
1435
  if (!choice) {
@@ -1049,7 +1456,7 @@ async function handleInteractiveMode(tui, session, config) {
1049
1456
  else disabledItems.push({
1050
1457
  value: key,
1051
1458
  label: chalk.gray(key),
1052
- description: chalk.gray("未配置 - Enter 设置 API Key")
1459
+ description: chalk.gray(t("settings.modelSelector.notConfigured"))
1053
1460
  });
1054
1461
  }
1055
1462
  }
@@ -1057,7 +1464,7 @@ async function handleInteractiveMode(tui, session, config) {
1057
1464
  if (modelItems.length === 0) {
1058
1465
  session.appendOutput([
1059
1466
  "",
1060
- " No models available from pi-ai registry",
1467
+ ` ${t("settings.cliMode.noModelsAvailable")}`,
1061
1468
  ""
1062
1469
  ]);
1063
1470
  continue;
@@ -1071,7 +1478,7 @@ async function handleInteractiveMode(tui, session, config) {
1071
1478
  setConfigValue(session, "llm.defaultModel", selectedKey);
1072
1479
  session.appendOutput([
1073
1480
  "",
1074
- ` [ok] Default model set to: ${selectedKey}`,
1481
+ ` ${t("settings.messages.defaultModelSet", { model: selectedKey })}`,
1075
1482
  ""
1076
1483
  ]);
1077
1484
  } else {
@@ -1091,7 +1498,7 @@ async function handleInteractiveMode(tui, session, config) {
1091
1498
  setConfigValue(session, "llm.defaultModel", selectedKey);
1092
1499
  session.appendOutput([
1093
1500
  "",
1094
- ` [ok] Default model set to: ${selectedKey}`,
1501
+ ` ${t("settings.messages.defaultModelSet", { model: selectedKey })}`,
1095
1502
  ""
1096
1503
  ]);
1097
1504
  }
@@ -1102,12 +1509,12 @@ async function handleInteractiveMode(tui, session, config) {
1102
1509
  return {
1103
1510
  value: `provider:${name}`,
1104
1511
  label: name,
1105
- description: hasK ? "key configured" : "no key"
1512
+ description: hasK ? t("settings.providerEntry.keyConfigured") : t("settings.providerEntry.noKey")
1106
1513
  };
1107
1514
  }), {
1108
1515
  value: "add-provider",
1109
- label: "Add Provider",
1110
- description: "Select from a list of known providers"
1516
+ label: t("settings.providerEntry.addProvider"),
1517
+ description: t("settings.providerEntry.addProviderDesc")
1111
1518
  }], { onExit: exitAll });
1112
1519
  if (!providerChoice) continue;
1113
1520
  const providerValue = providerChoice.value;
@@ -1116,22 +1523,22 @@ async function handleInteractiveMode(tui, session, config) {
1116
1523
  const action = await showSelectList(tui, [
1117
1524
  {
1118
1525
  value: "api-key",
1119
- label: "Configure API Key",
1526
+ label: t("settings.providerActions.configureApiKey"),
1120
1527
  description: ""
1121
1528
  },
1122
1529
  {
1123
1530
  value: "model",
1124
- label: "Select Model",
1531
+ label: t("settings.providerActions.selectModel"),
1125
1532
  description: ""
1126
1533
  },
1127
1534
  {
1128
1535
  value: "base-url",
1129
- label: "Base URL",
1536
+ label: t("settings.providerActions.baseUrl"),
1130
1537
  description: ""
1131
1538
  },
1132
1539
  {
1133
1540
  value: "set-default",
1134
- label: "Set as Default",
1541
+ label: t("settings.providerActions.setAsDefault"),
1135
1542
  description: ""
1136
1543
  }
1137
1544
  ], { onExit: exitAll });
@@ -1154,7 +1561,7 @@ async function handleInteractiveMode(tui, session, config) {
1154
1561
  const selected = await showSelectList(tui, KNOWN_PROVIDERS.map((p) => ({
1155
1562
  value: p.id,
1156
1563
  label: `${p.label} (${p.id})`,
1157
- description: p.apiFormat ? `API format: ${p.apiFormat}` : "OpenAI compatible"
1564
+ description: p.apiFormat ? t("settings.providerEntry.apiFormat", { format: p.apiFormat }) : t("settings.providerEntry.openaiCompatible")
1158
1565
  })), {
1159
1566
  maxVisible: 12,
1160
1567
  onExit: exitAll
@@ -1165,7 +1572,7 @@ async function handleInteractiveMode(tui, session, config) {
1165
1572
  if (existingProviders && providerName in existingProviders) {
1166
1573
  session.appendOutput([
1167
1574
  "",
1168
- ` ${providerName} already exists, use the provider entry to configure it`,
1575
+ ` ${providerName} ${t("settings.messages.alreadyExists")}`,
1169
1576
  ""
1170
1577
  ]);
1171
1578
  continue;
@@ -1179,22 +1586,39 @@ async function handleInteractiveMode(tui, session, config) {
1179
1586
  _setByDotPath(session.config, `llm.providers.${providerName}`, newProvider);
1180
1587
  session.appendOutput([
1181
1588
  "",
1182
- ` [ok] Added provider: ${providerName}`,
1589
+ ` ${t("settings.messages.providerAdded", { provider: providerName })}`,
1183
1590
  ""
1184
1591
  ]);
1185
1592
  if ((await showSelectList(tui, [{
1186
1593
  value: "yes",
1187
- label: "Yes, configure API Key now",
1188
- description: "Go to API Key settings"
1594
+ label: t("settings.addProvider.yes"),
1595
+ description: t("settings.addProvider.yesDesc")
1189
1596
  }, {
1190
1597
  value: "no",
1191
- label: "Later",
1192
- description: "Return to main menu"
1598
+ label: t("settings.addProvider.later"),
1599
+ description: t("settings.addProvider.laterDesc")
1193
1600
  }], { onExit: exitAll }))?.value === "yes") await handleApiKeyConfig(providerName, "");
1194
1601
  }
1195
1602
  } else if (value === "view-config") {
1196
1603
  const renderer = session.getRenderer();
1197
1604
  await showConfigView(tui, session.config, renderer);
1605
+ } else if (value === "language") {
1606
+ const currentLocale = String(_getByDotPath(state.current, "locale") ?? "zh-CN");
1607
+ const selected = await showSelectList(tui, SUPPORTED_LOCALES.map((loc) => ({
1608
+ ...loc,
1609
+ label: loc.value === currentLocale ? `${loc.label} ✓` : loc.label
1610
+ })), { onExit: exitAll });
1611
+ if (!selected || !selected.value) continue;
1612
+ if (selected.value !== currentLocale) {
1613
+ setConfigValue(session, "locale", selected.value);
1614
+ setLocale(selected.value);
1615
+ session.appendOutput([
1616
+ "",
1617
+ ` ${t("settings.messages.languageSet", { locale: selected.value })}`,
1618
+ ` ${t("settings.messages.restartRequired")}`,
1619
+ ""
1620
+ ]);
1621
+ }
1198
1622
  }
1199
1623
  }
1200
1624
  }
@@ -2198,7 +2622,7 @@ var OutputFormatter = class {
2198
2622
  "",
2199
2623
  ` 🍄 ${c.bold(`zapmyco@${version}`)}`,
2200
2624
  "",
2201
- " 欢迎回来!",
2625
+ ` ${t("output.welcome")}`,
2202
2626
  "",
2203
2627
  c.gray("─".repeat(90)),
2204
2628
  ""
@@ -2211,8 +2635,8 @@ var OutputFormatter = class {
2211
2635
  const zapmycoError = error;
2212
2636
  if (zapmycoError.code) {
2213
2637
  lines.push(`${c.red.bold(` ✗ [${zapmycoError.code}]`)} ${error.message}`);
2214
- if (zapmycoError.context && Object.keys(zapmycoError.context).length > 0) lines.push(c.gray(` 详情: ${JSON.stringify(zapmycoError.context)}`));
2215
- } else lines.push(`${c.red.bold("执行失败:")} ${error.message}`);
2638
+ if (zapmycoError.context && Object.keys(zapmycoError.context).length > 0) lines.push(c.gray(` ${t("output.error.detail")} ${JSON.stringify(zapmycoError.context)}`));
2639
+ } else lines.push(`${c.red.bold(`${t("output.error.executionFailed")}`)} ${error.message}`);
2216
2640
  lines.push("");
2217
2641
  return lines;
2218
2642
  }
@@ -2223,16 +2647,16 @@ var OutputFormatter = class {
2223
2647
  const lines = [
2224
2648
  "",
2225
2649
  c.gray(" ┌────────────────────────────────────────────┐"),
2226
- ` │ ${statusIcon} ${c.bold("执行完成")}`,
2650
+ ` │ ${statusIcon} ${c.bold(t("output.result.title"))}`,
2227
2651
  c.gray(" ├────────────────────────────────────────────┤"),
2228
- ` │ ${c.gray("目标:")} ${result.summary.slice(0, 40)}`,
2229
- ` │ ${c.gray("状态:")} ${result.overallStatus === "success" ? c.green("成功") : result.overallStatus === "partial-failure" ? c.yellow("部分成功") : c.red("失败")}`,
2230
- ` │ ${c.gray("耗时:")} ${(result.totalDuration / 1e3).toFixed(1)}s · ${c.gray("Token:")} ${result.totalTokenUsage.totalTokens.toLocaleString()}`,
2231
- ` │ ${c.gray("成本:")} $${result.totalTokenUsage.estimatedCostUsd.toFixed(4)}`
2652
+ ` │ ${c.gray(t("output.result.goal"))} ${result.summary.slice(0, 40)}`,
2653
+ ` │ ${c.gray(t("output.result.status"))} ${result.overallStatus === "success" ? c.green(t("output.result.success")) : result.overallStatus === "partial-failure" ? c.yellow(t("output.result.partialSuccess")) : c.red(t("output.result.failed"))}`,
2654
+ ` │ ${c.gray(t("output.result.duration"))} ${(result.totalDuration / 1e3).toFixed(1)}s · ${c.gray(t("output.result.token"))} ${result.totalTokenUsage.totalTokens.toLocaleString()}`,
2655
+ ` │ ${c.gray(t("output.result.cost"))} $${result.totalTokenUsage.estimatedCostUsd.toFixed(4)}`
2232
2656
  ];
2233
2657
  if (result.taskResults.length > 0) {
2234
2658
  lines.push(c.gray(" ├────────────────────────────────────────────┤"));
2235
- lines.push(` │ ${c.bold("任务拆分")} (${result.taskResults.length} 个子任务):`);
2659
+ lines.push(` │ ${c.bold(t("output.result.taskBreakdown"))} (${result.taskResults.length} ${t("output.result.subtaskCount")}):`);
2236
2660
  for (const tr of result.taskResults) {
2237
2661
  const icon = tr.status === "success" ? c.green("✓") : tr.status === "partial" ? c.yellow("~") : c.red("✗");
2238
2662
  lines.push(` │ ${icon} ${tr.taskId.slice(0, 12)}...`);
@@ -2240,7 +2664,7 @@ var OutputFormatter = class {
2240
2664
  }
2241
2665
  if (result.allArtifacts.length > 0) {
2242
2666
  lines.push(c.gray(" ├────────────────────────────────────────────┤"));
2243
- lines.push(` │ ${c.bold("制品:")}`);
2667
+ lines.push(` │ ${c.bold(t("output.result.artifacts"))}`);
2244
2668
  for (const artifact of result.allArtifacts) {
2245
2669
  const icon = artifact.type === "pull-request" ? "🔗" : "📄";
2246
2670
  lines.push(` │ ${icon} ${artifact.description} (${artifact.reference})`);
@@ -2248,7 +2672,7 @@ var OutputFormatter = class {
2248
2672
  }
2249
2673
  if (result.nextSteps && result.nextSteps.length > 0) {
2250
2674
  lines.push(c.gray(" ├────────────────────────────────────────────┤"));
2251
- lines.push(` │ ${c.bold("建议:")}`);
2675
+ lines.push(` │ ${c.bold(t("output.result.suggestions"))}`);
2252
2676
  for (let i = 0; i < result.nextSteps.length; i++) lines.push(` │ ${i + 1}. ${result.nextSteps[i]}`);
2253
2677
  }
2254
2678
  lines.push(c.gray(" └────────────────────────────────────────────┘"));
@@ -2260,14 +2684,17 @@ var OutputFormatter = class {
2260
2684
  const c = this.getColor();
2261
2685
  const lines = [
2262
2686
  "",
2263
- c.bold(" 📋 任务拆分概览"),
2264
- c.gray(` ${graph.nodes.size} 个子任务,${graph.layers.length} 层并行`),
2687
+ c.bold(` 📋 ${t("output.taskGraph.title")}`),
2688
+ c.gray(` ${t("output.taskGraph.total", {
2689
+ count: graph.nodes.size,
2690
+ layers: graph.layers.length
2691
+ })}`),
2265
2692
  ""
2266
2693
  ];
2267
2694
  for (let layerIdx = 0; layerIdx < graph.layers.length; layerIdx++) {
2268
2695
  const layer = graph.layers[layerIdx];
2269
2696
  if (!layer) continue;
2270
- lines.push(c.gray(` ${layerIdx + 1} 层 (可并行):`));
2697
+ lines.push(c.gray(` ${t("output.taskGraph.layer", { index: layerIdx + 1 })}`));
2271
2698
  for (const taskId of layer) {
2272
2699
  const task = graph.nodes.get(taskId);
2273
2700
  if (task) {
@@ -2284,15 +2711,15 @@ var OutputFormatter = class {
2284
2711
  const c = this.getColor();
2285
2712
  const lines = [
2286
2713
  "",
2287
- c.bold(" 🤖 已注册 Agent"),
2714
+ c.bold(` 🤖 ${t("output.agents.title")}`),
2288
2715
  ""
2289
2716
  ];
2290
2717
  if (agents.length === 0) {
2291
- lines.push(c.gray(" 暂无已注册的 Agent"));
2718
+ lines.push(c.gray(` ${t("output.agents.empty")}`));
2292
2719
  lines.push("");
2293
2720
  return lines;
2294
2721
  }
2295
- lines.push(` ${c.bold("ID").padEnd(20)} ${c.bold("状态").padEnd(10)} ${c.bold("负载").padEnd(8)} ${c.bold("能力")}`);
2722
+ lines.push(` ${c.bold(t("output.agents.id")).padEnd(20)} ${c.bold(t("output.agents.status")).padEnd(10)} ${c.bold(t("output.agents.load")).padEnd(8)} ${c.bold(t("output.agents.capability"))}`);
2296
2723
  lines.push(c.gray(` ${"─".repeat(60)}`));
2297
2724
  for (const agent of agents) {
2298
2725
  const statusDot = agent.status === "online" ? c.green("●") : agent.status === "busy" ? c.yellow("●") : c.gray("○");
@@ -2307,32 +2734,33 @@ var OutputFormatter = class {
2307
2734
  const c = this.getColor();
2308
2735
  const lines = [
2309
2736
  "",
2310
- c.bold(" ⚙️ 当前配置"),
2737
+ c.bold(` ⚙️ ${t("output.config.title")}`),
2311
2738
  "",
2312
- c.bold(" LLM:")
2739
+ c.bold(` ${t("output.config.llm")}`)
2313
2740
  ];
2314
- lines.push(` 默认模型: ${config.llm.defaultModel}`);
2741
+ lines.push(` ${t("output.config.defaultModel")} ${config.llm.defaultModel}`);
2315
2742
  const defaultModelKey = config.llm.defaultModel;
2316
2743
  const slashIdx = defaultModelKey.indexOf("/");
2317
2744
  const defaultProvider = slashIdx > 0 ? defaultModelKey.slice(0, slashIdx) : "anthropic";
2318
2745
  const defaultModelName = slashIdx > 0 ? defaultModelKey.slice(slashIdx + 1) : defaultModelKey;
2319
2746
  const providerConfig = config.llm.providers[defaultProvider];
2320
2747
  const modelConfig = providerConfig?.models?.[defaultModelName];
2321
- lines.push(` 提供商: ${defaultProvider}`);
2322
- lines.push(` 模型 ID: ${modelConfig?.id ?? defaultModelName}`);
2323
- if (modelConfig?.input && modelConfig.input.length > 0) lines.push(` 输入类型: ${modelConfig.input.join(", ")}`);
2324
- lines.push(` API Key: ${providerConfig?.apiKey ? c.gray("***已配置***") : c.red("(未配置)")}`);
2325
- if (providerConfig?.apiFormat) lines.push(` API 格式: ${providerConfig.apiFormat}`);
2326
- lines.push(c.bold(" 调度器:"));
2327
- lines.push(` 最大并行: ${config.scheduler.maxConcurrency}`);
2328
- lines.push(` Agent 最大并发: ${config.scheduler.maxPerAgent}`);
2329
- lines.push(` 任务超时: ${(config.scheduler.taskTimeoutMs / 1e3 / 60).toFixed(0)} 分钟`);
2330
- lines.push(` 最大重试: ${config.scheduler.maxRetries}`);
2331
- lines.push(c.bold(" CLI:"));
2332
- lines.push(` 颜色输出: ${config.cli.color ? c.green("开启") : c.gray("关闭")}`);
2333
- lines.push(` 调试模式: ${config.cli.debug ? c.green("开启") : c.gray("关闭")}`);
2334
- lines.push(` 输出格式: ${config.cli.outputFormat}`);
2335
- lines.push(c.bold(" Agents:"));
2748
+ lines.push(` ${t("output.config.provider")} ${defaultProvider}`);
2749
+ lines.push(` ${t("output.config.modelId")} ${modelConfig?.id ?? defaultModelName}`);
2750
+ if (modelConfig?.input && modelConfig.input.length > 0) lines.push(` ${t("output.config.inputType")} ${modelConfig.input.join(", ")}`);
2751
+ lines.push(` ${t("output.config.apiKey")} ${providerConfig?.apiKey ? c.gray(t("output.config.apiKeyConfigured")) : c.red(t("output.config.apiKeyNotConfigured"))}`);
2752
+ if (providerConfig?.apiFormat) lines.push(` ${t("output.config.apiFormat")} ${providerConfig.apiFormat}`);
2753
+ lines.push(c.bold(` ${t("output.config.scheduler")}`));
2754
+ lines.push(` ${t("output.config.maxConcurrency")} ${config.scheduler.maxConcurrency}`);
2755
+ lines.push(` ${t("output.config.maxPerAgent")} ${config.scheduler.maxPerAgent}`);
2756
+ lines.push(` ${t("output.config.taskTimeout")} ${(config.scheduler.taskTimeoutMs / 1e3 / 60).toFixed(0)} ${t("output.config.minutes")}`);
2757
+ lines.push(` ${t("output.config.maxRetries")} ${config.scheduler.maxRetries}`);
2758
+ lines.push(c.bold(` ${t("output.config.cli")}`));
2759
+ lines.push(` ${t("output.config.colorEnabled")}: ${config.cli.color ? c.green(t("output.config.colorEnabled")) : c.gray(t("output.config.colorDisabled"))}`);
2760
+ lines.push(` ${t("output.config.debugEnabled")}: ${config.cli.debug ? c.green(t("output.config.debugEnabled")) : c.gray(t("output.config.debugDisabled"))}`);
2761
+ lines.push(` ${t("output.config.outputFormat")} ${config.cli.outputFormat}`);
2762
+ lines.push(` ${t("output.config.uiLanguage")} ${config.locale ?? "zh-CN"}`);
2763
+ lines.push(c.bold(` ${t("output.config.agents")}`));
2336
2764
  for (const agent of config.agents) {
2337
2765
  const statusIcon = agent.enabled ? c.green("✓") : c.gray("✗");
2338
2766
  lines.push(` ${statusIcon} ${agent.id}`);
@@ -2345,11 +2773,11 @@ var OutputFormatter = class {
2345
2773
  const c = this.getColor();
2346
2774
  const lines = [
2347
2775
  "",
2348
- c.bold(" 📜 会话历史"),
2776
+ c.bold(` 📜 ${t("output.history.title")}`),
2349
2777
  ""
2350
2778
  ];
2351
2779
  if (entries.length === 0) {
2352
- lines.push(c.gray(" 暂无历史记录"));
2780
+ lines.push(c.gray(` ${t("output.history.empty")}`));
2353
2781
  lines.push("");
2354
2782
  return lines;
2355
2783
  }
@@ -2368,16 +2796,16 @@ var OutputFormatter = class {
2368
2796
  const c = this.getColor();
2369
2797
  const lines = [
2370
2798
  "",
2371
- c.bold(" 📊 会话状态"),
2799
+ c.bold(` 📊 ${t("output.status.title")}`),
2372
2800
  ""
2373
2801
  ];
2374
- const stateLabel = stats.state === "idle" ? c.green("空闲") : stats.state === "executing" ? c.magenta("执行中") : c.gray("关闭中");
2375
- lines.push(` 状态: ${stateLabel}`);
2376
- lines.push(` 总请求数: ${stats.totalRequests}`);
2377
- lines.push(` 成功: ${c.green(String(stats.successCount))}`);
2378
- lines.push(` 失败: ${stats.failureCount > 0 ? c.red(String(stats.failureCount)) : String(stats.failureCount)}`);
2379
- lines.push(` Token 消耗: ${stats.totalTokens.toLocaleString()}`);
2380
- lines.push(` 总成本: $${stats.totalCostUsd.toFixed(4)}`);
2802
+ const stateLabel = stats.state === "idle" ? c.green(t("output.status.idle")) : stats.state === "executing" ? c.magenta(t("output.status.executing")) : c.gray(t("output.status.closing"));
2803
+ lines.push(` ${t("output.status.state").padEnd(10)} ${stateLabel}`);
2804
+ lines.push(` ${t("output.status.totalRequests").padEnd(10)} ${stats.totalRequests}`);
2805
+ lines.push(` ${t("output.status.success").padEnd(10)} ${c.green(String(stats.successCount))}`);
2806
+ lines.push(` ${t("output.status.failure").padEnd(10)} ${stats.failureCount > 0 ? c.red(String(stats.failureCount)) : String(stats.failureCount)}`);
2807
+ lines.push(` ${t("output.status.tokenConsumption").padEnd(10)} ${stats.totalTokens.toLocaleString()}`);
2808
+ lines.push(` ${t("output.status.totalCost").padEnd(10)} $${stats.totalCostUsd.toFixed(4)}`);
2381
2809
  lines.push("");
2382
2810
  return lines;
2383
2811
  }
@@ -7550,8 +7978,8 @@ function getApiKeyErrorHelp(errorMessage) {
7550
7978
  const envVarName = `${providerName.toUpperCase().replace(/-/g, "_")}_API_KEY`;
7551
7979
  return [
7552
7980
  "",
7553
- chalk.yellow(` 请设置环境变量: export ${envVarName}=<your-api-key>`),
7554
- chalk.yellow(` 或在 REPL 中使用: /config set llm.providers.${providerName}.apiKey <your-key>`)
7981
+ chalk.yellow(` ${t("session.setEnvVarHint", { envVar: envVarName })}`),
7982
+ chalk.yellow(` ${t("session.useConfigHint", { provider: providerName })}`)
7555
7983
  ];
7556
7984
  }
7557
7985
  /**
@@ -7686,7 +8114,8 @@ var ReplSession = class {
7686
8114
  async start() {
7687
8115
  this._state = "idle";
7688
8116
  this.updateStatsState();
7689
- this.outputArea.append(["ZapMyco: 欢迎回来!", ""]);
8117
+ setLocale(this.config.locale ?? "zh-CN");
8118
+ this.outputArea.append([t("session.welcome"), ""]);
7690
8119
  this.tui.start();
7691
8120
  }
7692
8121
  /** 优雅关闭会话 */
@@ -7696,6 +8125,7 @@ var ReplSession = class {
7696
8125
  this.updateStatsState();
7697
8126
  log.info("REPL 关闭", { reason: reason ?? "未知" });
7698
8127
  this.cancelCurrentTask();
8128
+ this.editor.setExecuting(false);
7699
8129
  eventBus.emit("system:shutdown", { reason });
7700
8130
  if (this.cronScheduler) {
7701
8131
  this.cronScheduler.stop();
@@ -7706,6 +8136,7 @@ var ReplSession = class {
7706
8136
  this.mcpManager = null;
7707
8137
  }
7708
8138
  this.tui.stop();
8139
+ process.exit(0);
7709
8140
  }
7710
8141
  /** 获取渲染器引用 */
7711
8142
  getRenderer() {
@@ -7856,8 +8287,8 @@ var ReplSession = class {
7856
8287
  clearInterval(spinnerInterval);
7857
8288
  if (outputText) this.outputArea.replaceLastLine(responseStyle(ZAPMYCO_PREFIX + outputText));
7858
8289
  else if (taskResult.status !== "success") {
7859
- const errorMsg = taskResult.error?.message ?? "Agent 执行失败(无详细错误信息)";
7860
- this.outputArea.replaceLastLine(chalk.red(`ZapMyco: [错误] ${errorMsg}`));
8290
+ const errorMsg = taskResult.error?.message ?? t("session.agentErrorMessage");
8291
+ this.outputArea.replaceLastLine(chalk.red(`ZapMyco: ${t("session.errorPrefix")} ${errorMsg}`));
7861
8292
  const helpLines = getApiKeyErrorHelp(errorMsg);
7862
8293
  if (helpLines.length > 0) {
7863
8294
  this.outputArea.append(helpLines);
@@ -7893,11 +8324,11 @@ var ReplSession = class {
7893
8324
  this.outputArea.append([""]);
7894
8325
  }
7895
8326
  }
7896
- } else this.outputArea.replaceLastLine(chalk.red("ZapMyco: [错误] 模型未返回任何内容,请检查 API Key 配置"));
8327
+ } else this.outputArea.replaceLastLine(chalk.red(`ZapMyco: ${t("session.errorPrefix")} ${t("session.noContentError")}`));
7897
8328
  }
7898
8329
  if (taskResult.status !== "success") {
7899
- const errorMsg = taskResult.error?.message ?? "Agent 执行失败(无详细错误信息)";
7900
- if (!spinnerActive || outputText) this.outputArea.appendText(`[错误] ${errorMsg}`);
8330
+ const errorMsg = taskResult.error?.message ?? t("session.agentErrorMessage");
8331
+ if (!spinnerActive || outputText) this.outputArea.appendText(`${t("session.errorPrefix")} ${errorMsg}`);
7901
8332
  log.error("Agent 执行返回 failure", {
7902
8333
  taskId,
7903
8334
  error: taskResult.error,
@@ -7951,7 +8382,7 @@ var ReplSession = class {
7951
8382
  goalId: `goal-${startTime}`,
7952
8383
  error: err
7953
8384
  });
7954
- this.outputArea.replaceLastLine(responseStyle(`${ZAPMYCO_PREFIX}[错误] ${err.message}`));
8385
+ this.outputArea.replaceLastLine(responseStyle(`${ZAPMYCO_PREFIX}${t("session.errorPrefix")} ${err.message}`));
7955
8386
  const helpLines = getApiKeyErrorHelp(err.message);
7956
8387
  if (helpLines.length > 0) {
7957
8388
  this.outputArea.append(helpLines);
@@ -8083,7 +8514,7 @@ var ReplSession = class {
8083
8514
  createReplAgent() {
8084
8515
  const agent = createLlmBasedAgent({
8085
8516
  agentId: "repl-chat-agent",
8086
- displayName: "Zapmyco AI 助手",
8517
+ displayName: t("session.displayName"),
8087
8518
  capabilities: [{
8088
8519
  id: "chat",
8089
8520
  name: "对话",
@@ -8200,6 +8631,8 @@ var ReplSession = class {
8200
8631
  }
8201
8632
  ctrlCPressCount++;
8202
8633
  if (ctrlCPressCount >= 2) {
8634
+ clearTimeout(ctrlCTimer);
8635
+ ctrlCTimer = void 0;
8203
8636
  this.shutdown("用户连续按下 Ctrl+C");
8204
8637
  return;
8205
8638
  }
@@ -8265,7 +8698,7 @@ var ReplSession = class {
8265
8698
  const err = result.error;
8266
8699
  if (err.code === "ENOENT") this.outputArea.append([
8267
8700
  "",
8268
- `未找到编辑器: ${editorCmd},请设置 $EDITOR 环境变量`,
8701
+ t("session.editorNotFound", { cmd: editorCmd }),
8269
8702
  ""
8270
8703
  ]);
8271
8704
  else this.outputArea.append([
@@ -8278,7 +8711,7 @@ var ReplSession = class {
8278
8711
  const message = err instanceof Error ? err.message : String(err);
8279
8712
  this.outputArea.append([
8280
8713
  "",
8281
- `打开编辑器失败: ${message}`,
8714
+ t("session.editorFailed", { message }),
8282
8715
  ""
8283
8716
  ]);
8284
8717
  } finally {
@@ -8348,7 +8781,7 @@ async function startRepl() {
8348
8781
  * zapmyco version 显示版本号
8349
8782
  */
8350
8783
  const program = new Command();
8351
- program.name(APP_NAME).description("AI 原生并行任务编排系统 -- AI 总管").version(__VERSION__, "-v, --version", "显示版本号").helpOption("-h, --help", "显示帮助信息");
8784
+ program.name(APP_NAME).description("AI 原生并行任务编排系统 -- AI 总管").version(VERSION, "-v, --version", "显示版本号").helpOption("-h, --help", "显示帮助信息");
8352
8785
  program.action(async () => {
8353
8786
  try {
8354
8787
  await startRepl();