@goodandready/dsh-goal 0.1.8 → 0.1.10

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/lib/client.js CHANGED
@@ -789,14 +789,14 @@ window.__ModuleLoader__.load({
789
789
  isCompleted
790
790
  ? React.createElement(IconCheck, { size: 18, className: 'cb-badge-ok' })
791
791
  : React.createElement(IconTarget, { size: 18 }),
792
- isCompleted ? (t('modalTitleCompleted') || 'Цель выполнена: план и результаты') : (t('modalTitle') || 'План и статус цели'),
792
+ isCompleted ? (t('modalTitleCompleted') || 'Goal completed: plan and results') : (t('modalTitle') || 'Goal plan and status'),
793
793
  ),
794
794
  React.createElement(
795
795
  'button',
796
796
  {
797
797
  className: 'dsh-goal-btn icon-only close',
798
798
  onClick: onClose,
799
- title: t('close') || 'Закрыть',
799
+ title: t('close') || 'Close',
800
800
  },
801
801
  '✕',
802
802
  ),
@@ -817,39 +817,39 @@ window.__ModuleLoader__.load({
817
817
  'div',
818
818
  { className: 'dsh-goal-stat-box' },
819
819
  React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedElapsed || '0s'),
820
- React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || 'Время работы'),
820
+ React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || 'Running time'),
821
821
  ),
822
822
  React.createElement(
823
823
  'div',
824
824
  { className: 'dsh-goal-stat-box' },
825
- React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? 'Готово' : '—')),
826
- React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || 'Оценка (ETA)'),
825
+ React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? (t('done') || 'Done') : '—')),
826
+ React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || 'ETA'),
827
827
  ),
828
828
  React.createElement(
829
829
  'div',
830
830
  { className: 'dsh-goal-stat-box', title: tokensTooltip },
831
831
  React.createElement('span', { className: 'dsh-goal-stat-val' }, tokensFormatted),
832
- React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || 'Токены'),
832
+ React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || 'Tokens'),
833
833
  ),
834
834
  React.createElement(
835
835
  'div',
836
836
  { className: 'dsh-goal-stat-box' },
837
837
  React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.iterationsCount || 0}/${state.maxIterations || 25}`),
838
- React.createElement('span', { className: 'dsh-goal-stat-lbl' }, 'Итерации'),
838
+ React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('iterations') || 'Iterations'),
839
839
  ),
840
840
  React.createElement(
841
841
  'div',
842
842
  { className: 'dsh-goal-stat-box' },
843
843
  React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.progressPercent || 0}%`),
844
- React.createElement('span', { className: 'dsh-goal-stat-lbl' }, 'Прогресс'),
844
+ React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('progress') || 'Progress'),
845
845
  ),
846
846
  ),
847
847
  React.createElement(
848
848
  'div',
849
849
  { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 } },
850
- React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || 'План работ:'),
850
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || 'Plan of work:'),
851
851
  milestones.length === 0
852
- ? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || 'Агент формирует план работ...')
852
+ ? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || 'Agent is preparing the plan of work...')
853
853
  : milestones.map((m, i) =>
854
854
  React.createElement(
855
855
  'div',
@@ -1036,13 +1036,33 @@ window.__ModuleLoader__.load({
1036
1036
  stateRef.current = state;
1037
1037
  const lastStateRef = useRef(null);
1038
1038
 
1039
- const t = (key) => {
1039
+ const resolveLocale = () => {
1040
+ let active = 'en';
1040
1041
  if (ctx?.locale?.getSnapshot) {
1041
- const snap = ctx.locale.getSnapshot();
1042
- const active = snap?.active || 'ru';
1043
- return LOCALES[active]?.[key] || LOCALES.ru[key] || LOCALES.en[key] || key;
1042
+ const snapLoc = ctx.locale.getSnapshot();
1043
+ if (snapLoc?.active) {
1044
+ const code = snapLoc.active.toLowerCase();
1045
+ if (code.startsWith('zh')) active = 'zh';
1046
+ else if (code.startsWith('ru')) active = 'ru';
1047
+ else active = 'en';
1048
+ }
1049
+ } else if (typeof navigator !== 'undefined' && navigator.language) {
1050
+ const code = navigator.language.toLowerCase();
1051
+ if (code.startsWith('zh')) active = 'zh';
1052
+ else if (code.startsWith('ru')) active = 'ru';
1053
+ else active = 'en';
1044
1054
  }
1045
- return LOCALES.ru[key] || LOCALES.en[key] || key;
1055
+ if (state?.lang) {
1056
+ if (state.lang === 'zh') active = 'zh';
1057
+ else if (state.lang === 'ru') active = 'ru';
1058
+ else if (state.lang === 'en') active = 'en';
1059
+ }
1060
+ return active;
1061
+ };
1062
+
1063
+ const t = (key) => {
1064
+ const active = resolveLocale();
1065
+ return LOCALES[active]?.[key] || LOCALES.en?.[key] || LOCALES.ru?.[key] || key;
1046
1066
  };
1047
1067
 
1048
1068
  useEffect(() => {
@@ -1310,7 +1330,7 @@ window.__ModuleLoader__.load({
1310
1330
  {
1311
1331
  className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isPaused ? 'dsh-goal-badge-warn' : ''}`,
1312
1332
  },
1313
- isCompleted ? (t('goalCompleted') || 'Цель выполнена') : isPaused ? (t('pause') || 'На паузе') : (t('goalLabel') || 'Текущая цель'),
1333
+ isCompleted ? (t('goalCompleted') || 'Goal completed') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Current goal'),
1314
1334
  ),
1315
1335
  React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
1316
1336
  ),
@@ -1327,7 +1347,7 @@ window.__ModuleLoader__.load({
1327
1347
  'button',
1328
1348
  {
1329
1349
  className: 'dsh-goal-btn icon-only',
1330
- title: isPaused ? (t('resume') || 'Возобновить') : (t('pause') || 'Приостановить'),
1350
+ title: isPaused ? (t('resume') || 'Resume') : (t('pause') || 'Pause'),
1331
1351
  onClick: () => handleAction(isPaused ? 'resume' : 'pause'),
1332
1352
  },
1333
1353
  isPaused ? React.createElement(IconPlay, { size: 14 }) : React.createElement(IconPause, { size: 14 }),
@@ -1337,7 +1357,7 @@ window.__ModuleLoader__.load({
1337
1357
  'button',
1338
1358
  {
1339
1359
  className: 'dsh-goal-btn icon-only',
1340
- title: t('details') || 'Развернуть детали цели',
1360
+ title: t('details') || 'Expand goal details',
1341
1361
  onClick: () => setIsModalOpen(true),
1342
1362
  },
1343
1363
  React.createElement(IconExpand, { size: 14 }),
@@ -1347,7 +1367,7 @@ window.__ModuleLoader__.load({
1347
1367
  'button',
1348
1368
  {
1349
1369
  className: 'dsh-goal-btn icon-only close',
1350
- title: t('closeBanner') || 'Закрыть плашку цели',
1370
+ title: t('closeBanner') || 'Close goal banner',
1351
1371
  onClick: () => handleAction('clear'),
1352
1372
  },
1353
1373
  '✕',
@@ -1475,13 +1495,28 @@ window.__ModuleLoader__.load({
1475
1495
  });
1476
1496
  }, [scope]);
1477
1497
 
1478
- const t = (key) => {
1498
+ const resolveLocale = () => {
1499
+ let active = 'en';
1479
1500
  if (ctx?.locale?.getSnapshot) {
1480
1501
  const snapLoc = ctx.locale.getSnapshot();
1481
- const active = snapLoc?.active || 'ru';
1482
- return LOCALES[active]?.[key] || LOCALES.ru[key] || LOCALES.en[key] || key;
1502
+ if (snapLoc?.active) {
1503
+ const code = snapLoc.active.toLowerCase();
1504
+ if (code.startsWith('zh')) active = 'zh';
1505
+ else if (code.startsWith('ru')) active = 'ru';
1506
+ else active = 'en';
1507
+ }
1508
+ } else if (typeof navigator !== 'undefined' && navigator.language) {
1509
+ const code = navigator.language.toLowerCase();
1510
+ if (code.startsWith('zh')) active = 'zh';
1511
+ else if (code.startsWith('ru')) active = 'ru';
1512
+ else active = 'en';
1483
1513
  }
1484
- return LOCALES.ru[key] || LOCALES.en[key] || key;
1514
+ return active;
1515
+ };
1516
+
1517
+ const t = (key) => {
1518
+ const active = resolveLocale();
1519
+ return LOCALES[active]?.[key] || LOCALES.en[key] || LOCALES.ru?.[key] || key;
1485
1520
  };
1486
1521
 
1487
1522
  const status = (snap && snap.status) || 'ready';
@@ -1733,50 +1768,13 @@ window.__ModuleLoader__.load({
1733
1768
 
1734
1769
  // --- СЛОВАРИ ЛОКАЛИЗАЦИИ ---
1735
1770
  const LOCALES = {
1736
- ru: {
1737
- goalLabel: 'Текущая цель',
1738
- goalCompleted: 'Цель выполнена',
1739
- clearGoal: 'Очистить цель',
1740
- closeBanner: 'Закрыть плашку цели',
1741
- pause: 'Приостановить',
1742
- resume: 'Возобновить',
1743
- details: 'Развернуть детали цели',
1744
- modalTitle: 'План и статус цели',
1745
- modalTitleCompleted: 'Цель выполнена: план и результаты',
1746
- time: 'Время работы',
1747
- eta: 'Оценка (ETA)',
1748
- tokens: 'Токены',
1749
- milestones: 'План работ:',
1750
- noMilestones: 'Агент формирует план работ...',
1751
- close: 'Закрыть',
1752
- pluginTitle: 'Цели и автономный режим (Goal Mode)',
1753
- pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
1754
- maxIterLabel: 'Максимум итераций (Safety Limit):',
1755
- autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
1756
- soundLabel: 'Звук по завершении цели',
1757
- quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
1758
- quickLaunch: 'Запустить цель',
1759
- quickLaunchTitle: 'Быстрый запуск цели',
1760
- quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
1761
- quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
1762
- startGoal: 'Запустить цель',
1763
- copyReport: '📋 Скопировать отчёт в Markdown',
1764
- reportCopied: '✅ Скопировано!',
1765
- save: 'Сохранить',
1766
- saving: 'Сохранение…',
1767
- discard: 'Отменить правки',
1768
- resetField: 'По умолчанию',
1769
- overridden: 'изменено',
1770
- saveFailed: 'Не сохранено — исправьте и повторите',
1771
- loading: 'Загрузка настроек…',
1772
- unavailable: 'Настройки недоступны',
1773
- },
1774
1771
  en: {
1775
1772
  goalLabel: 'Current goal',
1776
1773
  goalCompleted: 'Goal completed',
1777
1774
  clearGoal: 'Clear goal',
1778
1775
  closeBanner: 'Close goal banner',
1779
1776
  pause: 'Pause',
1777
+ paused: 'Paused',
1780
1778
  resume: 'Resume',
1781
1779
  details: 'Expand Goal Details',
1782
1780
  modalTitle: 'Goal Plan & Status',
@@ -1784,6 +1782,9 @@ window.__ModuleLoader__.load({
1784
1782
  time: 'Running time',
1785
1783
  eta: 'ETA',
1786
1784
  tokens: 'Tokens',
1785
+ iterations: 'Iterations',
1786
+ progress: 'Progress',
1787
+ done: 'Done',
1787
1788
  milestones: 'Plan of work:',
1788
1789
  noMilestones: 'Agent is preparing the plan of work...',
1789
1790
  close: 'Close',
@@ -1809,6 +1810,90 @@ window.__ModuleLoader__.load({
1809
1810
  loading: 'Loading settings…',
1810
1811
  unavailable: 'Settings unavailable',
1811
1812
  },
1813
+ zh: {
1814
+ goalLabel: '当前目标',
1815
+ goalCompleted: '目标已完成',
1816
+ clearGoal: '清除目标',
1817
+ closeBanner: '关闭目标横幅',
1818
+ pause: '暂停',
1819
+ paused: '已暂停',
1820
+ resume: '恢复',
1821
+ details: '展开目标详情',
1822
+ modalTitle: '目标计划与状态',
1823
+ modalTitleCompleted: '目标已完成:计划与成果',
1824
+ time: '运行时间',
1825
+ eta: '预估剩余 (ETA)',
1826
+ tokens: 'Token 消耗',
1827
+ iterations: '迭代次数',
1828
+ progress: '进度',
1829
+ done: '已完成',
1830
+ milestones: '工作计划:',
1831
+ noMilestones: '智能体正在制定工作计划...',
1832
+ close: '关闭',
1833
+ pluginTitle: '目标模式与自主循环 (Goal Mode)',
1834
+ pluginDesc: '输入框上方常驻目标横幅、里程碑拆解与自主循环',
1835
+ maxIterLabel: '最大迭代次数 (Safety Limit):',
1836
+ autoDriveLabel: '自动执行:在各轮次间自动保持循环',
1837
+ soundLabel: '目标完成时播放提示音',
1838
+ quickLaunchLabel: '在输入框上方显示快速启动目标按钮',
1839
+ quickLaunch: '启动目标',
1840
+ quickLaunchTitle: '快速启动目标',
1841
+ quickLaunchDesc: '为自主模式下的智能体设定任务目标:',
1842
+ quickLaunchPlaceholder: '例如:实现报表导出功能并编写单元测试...',
1843
+ startGoal: '启动目标',
1844
+ copyReport: '📋 复制 Markdown 报告',
1845
+ reportCopied: '✅ 已复制!',
1846
+ save: '保存',
1847
+ saving: '保存中…',
1848
+ discard: '放弃更改',
1849
+ resetField: '恢复默认',
1850
+ overridden: '已修改',
1851
+ saveFailed: '保存失败 — 请检查并重试',
1852
+ loading: '正在加载设置…',
1853
+ unavailable: '设置不可用',
1854
+ },
1855
+ ru: {
1856
+ goalLabel: 'Текущая цель',
1857
+ goalCompleted: 'Цель выполнена',
1858
+ clearGoal: 'Очистить цель',
1859
+ closeBanner: 'Закрыть плашку цели',
1860
+ pause: 'Приостановить',
1861
+ paused: 'На паузе',
1862
+ resume: 'Возобновить',
1863
+ details: 'Развернуть детали цели',
1864
+ modalTitle: 'План и статус цели',
1865
+ modalTitleCompleted: 'Цель выполнена: план и результаты',
1866
+ time: 'Время работы',
1867
+ eta: 'Оценка (ETA)',
1868
+ tokens: 'Токены',
1869
+ iterations: 'Итерации',
1870
+ progress: 'Прогресс',
1871
+ done: 'Готово',
1872
+ milestones: 'План работ:',
1873
+ noMilestones: 'Агент формирует план работ...',
1874
+ close: 'Закрыть',
1875
+ pluginTitle: 'Цели и автономный режим (Goal Mode)',
1876
+ pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
1877
+ maxIterLabel: 'Максимум итераций (Safety Limit):',
1878
+ autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
1879
+ soundLabel: 'Звук по завершении цели',
1880
+ quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
1881
+ quickLaunch: 'Запустить цель',
1882
+ quickLaunchTitle: 'Быстрый запуск цели',
1883
+ quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
1884
+ quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
1885
+ startGoal: 'Запустить цель',
1886
+ copyReport: '📋 Скопировать отчёт в Markdown',
1887
+ reportCopied: '✅ Скопировано!',
1888
+ save: 'Сохранить',
1889
+ saving: 'Сохранение…',
1890
+ discard: 'Отменить правки',
1891
+ resetField: 'По умолчанию',
1892
+ overridden: 'изменено',
1893
+ saveFailed: 'Не сохранено — исправьте и повторите',
1894
+ loading: 'Загрузка настроек…',
1895
+ unavailable: 'Настройки недоступны',
1896
+ },
1812
1897
  };
1813
1898
 
1814
1899
  module.exports.inject = ['slots', 'locale', 'settingsScope'];
@@ -1,7 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { GoalState } from './goal-engine.js';
2
+ import { GoalState, detectLanguage } from './goal-engine.js';
3
3
 
4
- export const USAGE = 'Использование: /goal [<цель>|clear|pause|resume]';
4
+ export const USAGE_RU = 'Использование: /goal [<цель>|clear|pause|resume]';
5
+ export const USAGE_EN = 'Usage: /goal [<goal>|clear|pause|resume]';
6
+ export const USAGE = USAGE_RU;
5
7
 
6
8
  /**
7
9
  * Парсинг строки ввода для команды /goal
@@ -39,15 +41,26 @@ export function createGoalUserMessage(text) {
39
41
  /**
40
42
  * Формирование стартового промпта с жестким требованием составить план работ
41
43
  * @param {string} goalText
44
+ * @param {string} [explicitLang]
42
45
  * @returns {string}
43
46
  */
44
- export function formatGoalStartPrompt(goalText) {
45
- return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
46
- `СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
47
- `1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
48
- `2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
49
- `3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
50
- `4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
47
+ export function formatGoalStartPrompt(goalText, explicitLang) {
48
+ const lang = explicitLang || detectLanguage(goalText);
49
+ if (lang === 'ru') {
50
+ return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
51
+ `СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
52
+ `1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
53
+ `2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
54
+ `3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
55
+ `4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
56
+ }
57
+
58
+ return `🎯 Goal Mode activated: "${goalText}"\n\n` +
59
+ `STRICT AUTONOMOUS CONTRACT:\n` +
60
+ `1. MANDATORY STEP 1: Your FIRST action BEFORE doing any other work or reply MUST be calling tool goal_set_milestones with an array of 3-7 concrete, sequential milestones. The user sees this plan in real time.\n` +
61
+ `2. STEP 2: After establishing the plan, proceed to execute milestones. Before starting each milestone, mark it as in_progress via goal_update_progress(milestone_id, "in_progress").\n` +
62
+ `3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
63
+ `4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
51
64
  }
52
65
 
53
66
  /**
@@ -63,33 +76,49 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
63
76
  const sid = sessionId || 'default';
64
77
  const snap = engine.getSnapshot(sid);
65
78
 
79
+ const lang = (parsed.text ? detectLanguage(parsed.text) : (snap.lang || (snap.title ? detectLanguage(snap.title) : 'en'))) || 'en';
80
+ const isRu = lang === 'ru';
81
+ const usage = isRu ? USAGE_RU : USAGE_EN;
82
+
66
83
  switch (parsed.action) {
67
84
  case 'show': {
68
85
  if (!snap.hasActiveGoal) {
69
86
  return {
70
87
  kind: 'success',
71
- text: `🎯 Режим цели (Goal Mode): цель не установлена.\n${USAGE}`,
88
+ text: isRu
89
+ ? `🎯 Режим цели (Goal Mode): цель не установлена.\n${usage}`
90
+ : `🎯 Goal Mode: no active goal set.\n${usage}`,
72
91
  };
73
92
  }
74
93
 
75
94
  const milestonesInfo = snap.milestones.length > 0
76
- ? `\nПлан работ:\n` + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
95
+ ? (isRu ? `\nПлан работ:\n` : `\nWork plan:\n`) + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
77
96
  : '';
78
97
 
79
98
  const etaText = snap.formattedETA ? ` (ETA: ${snap.formattedETA})` : '';
80
99
  const tokensInfo = (snap.tokensUsage && snap.tokensUsage.totalTokens > 0)
81
- ? `\nТокены: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`
100
+ ? (isRu
101
+ ? `\nТокены: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`
102
+ : `\nTokens: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`)
82
103
  : '';
83
104
 
84
105
  return {
85
106
  kind: 'success',
86
- text: `🎯 Цель: «${snap.title}»\n` +
87
- `Статус: ${snap.state}\n` +
88
- `Время: ${snap.formattedElapsed}${etaText}\n` +
89
- `Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
90
- tokensInfo +
91
- milestonesInfo +
92
- `\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`,
107
+ text: isRu
108
+ ? `🎯 Цель: «${snap.title}»\n` +
109
+ `Статус: ${snap.state}\n` +
110
+ `Время: ${snap.formattedElapsed}${etaText}\n` +
111
+ `Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
112
+ tokensInfo +
113
+ milestonesInfo +
114
+ `\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`
115
+ : `🎯 Goal: "${snap.title}"\n` +
116
+ `Status: ${snap.state}\n` +
117
+ `Duration: ${snap.formattedElapsed}${etaText}\n` +
118
+ `Iterations: ${snap.iterationsCount}/${snap.maxIterations}` +
119
+ tokensInfo +
120
+ milestonesInfo +
121
+ `\n\nCommands: /goal pause, /goal resume, /goal clear, /goal <new goal>`,
93
122
  };
94
123
  }
95
124
 
@@ -97,7 +126,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
97
126
  if (!snap.hasActiveGoal) {
98
127
  return {
99
128
  kind: 'success',
100
- text: 'Цель не была установлена.',
129
+ text: isRu ? 'Цель не была установлена.' : 'No active goal set.',
101
130
  };
102
131
  }
103
132
  engine.clear(sid);
@@ -110,7 +139,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
110
139
  }
111
140
  return {
112
141
  kind: 'success',
113
- text: '🎯 Цель сброшена.',
142
+ text: isRu ? '🎯 Цель сброшена.' : '🎯 Goal cleared.',
114
143
  };
115
144
  }
116
145
 
@@ -118,16 +147,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
118
147
  if (!snap.hasActiveGoal) {
119
148
  return {
120
149
  kind: 'error',
121
- text: `Нельзя приостановить: активная цель отсутствует.\n${USAGE}`,
150
+ text: isRu
151
+ ? `Нельзя приостановить: активная цель отсутствует.\n${usage}`
152
+ : `Cannot pause: no active goal.\n${usage}`,
122
153
  };
123
154
  }
124
155
  if (snap.state === GoalState.PAUSED) {
125
156
  return {
126
157
  kind: 'success',
127
- text: '⏸ Цель уже на паузе.',
158
+ text: isRu ? '⏸ Цель уже на паузе.' : '⏸ Goal is already paused.',
128
159
  };
129
160
  }
130
- engine.pause('Пауза по команде пользователя (/goal pause)', sid);
161
+ engine.pause(isRu ? 'Пауза по команде пользователя (/goal pause)' : 'Paused by user command (/goal pause)', sid);
131
162
  if (agent && typeof agent.cancel === 'function') {
132
163
  try {
133
164
  agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
@@ -137,7 +168,9 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
137
168
  }
138
169
  return {
139
170
  kind: 'success',
140
- text: `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`,
171
+ text: isRu
172
+ ? `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`
173
+ : `⏸ Goal paused: "${snap.title}". Agent stopped. Use /goal resume or ▶️ button to continue.`,
141
174
  };
142
175
  }
143
176
 
@@ -145,19 +178,23 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
145
178
  if (!snap.hasActiveGoal) {
146
179
  return {
147
180
  kind: 'error',
148
- text: `Нельзя возобновить: активная цель отсутствует.\n${USAGE}`,
181
+ text: isRu
182
+ ? `Нельзя возобновить: активная цель отсутствует.\n${usage}`
183
+ : `Cannot resume: no active goal.\n${usage}`,
149
184
  };
150
185
  }
151
186
  if (snap.state === GoalState.RUNNING) {
152
187
  return {
153
188
  kind: 'success',
154
- text: '▶️ Цель уже выполняется.',
189
+ text: isRu ? '▶️ Цель уже выполняется.' : '▶️ Goal is already running.',
155
190
  };
156
191
  }
157
192
  engine.resume(sid);
158
193
  if (agent && typeof agent.followup === 'function') {
159
194
  try {
160
- const resumePrompt = `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`;
195
+ const resumePrompt = isRu
196
+ ? `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`
197
+ : `▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.`;
161
198
  agent.followup(createGoalUserMessage(resumePrompt));
162
199
  } catch (err) {
163
200
  console.warn('[dsh-goal] Failed to followup agent on resume:', err);
@@ -165,18 +202,20 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
165
202
  }
166
203
  return {
167
204
  kind: 'success',
168
- text: `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`,
205
+ text: isRu
206
+ ? `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`
207
+ : `▶️ Goal resumed: "${snap.title}". Agent continues working.`,
169
208
  };
170
209
  }
171
210
 
172
211
  case 'start': {
173
212
  const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
174
- const newSnap = engine.startGoal(parsed.text, { maxIterations }, sid);
213
+ const newSnap = engine.startGoal(parsed.text, { maxIterations, lang }, sid);
175
214
 
176
215
  // Если команда вызвана в контексте агента, отправляем цель в очередь модели
177
216
  if (agent && typeof agent.followup === 'function') {
178
217
  try {
179
- const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text));
218
+ const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text, lang));
180
219
  agent.followup(userMsg);
181
220
  } catch (err) {
182
221
  console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
@@ -185,15 +224,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
185
224
 
186
225
  return {
187
226
  kind: 'success',
188
- text: `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
189
- `Для паузы: /goal pause | Для сброса: /goal clear`,
227
+ text: isRu
228
+ ? `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
229
+ `Для паузы: /goal pause | Для сброса: /goal clear`
230
+ : `🎯 Goal activated: "${newSnap.title}" (max ${newSnap.maxIterations} iterations).\n` +
231
+ `To pause: /goal pause | To clear: /goal clear`,
190
232
  };
191
233
  }
192
234
 
193
235
  default:
194
236
  return {
195
237
  kind: 'error',
196
- text: `Неизвестная команда. ${USAGE}`,
238
+ text: isRu ? `Неизвестная команда. ${usage}` : `Unknown command. ${usage}`,
197
239
  };
198
240
  }
199
241
  }