zames_pro 2.50.2 → 2.52.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.
- package/README.md +22 -1
- package/dist/commands.js +73 -45
- package/dist/config-menu.js +143 -21
- package/dist/i18n.js +152 -0
- package/dist/index.js +104 -27
- package/dist/input.js +208 -6
- package/dist/sessions.js +26 -0
- package/package.json +1 -1
package/dist/i18n.js
CHANGED
|
@@ -39,6 +39,10 @@ const CATALOG = {
|
|
|
39
39
|
'common.unknown': { ru: 'неизвестно', en: 'unknown' },
|
|
40
40
|
// ---------- help ----------
|
|
41
41
|
'help.options': { ru: 'Опции CLI:', en: 'CLI options:' },
|
|
42
|
+
'help.color_note': {
|
|
43
|
+
ru: 'Цвет отключается переменной окружения NO_COLOR=1.',
|
|
44
|
+
en: 'Disable color with the NO_COLOR=1 environment variable.',
|
|
45
|
+
},
|
|
42
46
|
'help.opt.dir': {
|
|
43
47
|
ru: 'рабочая директория агента',
|
|
44
48
|
en: 'agent working directory',
|
|
@@ -95,6 +99,14 @@ const CATALOG = {
|
|
|
95
99
|
ru: '↑ / ↓ история введённых сообщений',
|
|
96
100
|
en: '↑ / ↓ input history',
|
|
97
101
|
},
|
|
102
|
+
'help.key.search': {
|
|
103
|
+
ru: 'Ctrl+R поиск по истории (обратный)',
|
|
104
|
+
en: 'Ctrl+R reverse-search the history',
|
|
105
|
+
},
|
|
106
|
+
'help.key.undo': {
|
|
107
|
+
ru: 'Ctrl+_ отменить правку в строке ввода',
|
|
108
|
+
en: 'Ctrl+_ undo the last edit in the input line',
|
|
109
|
+
},
|
|
98
110
|
'help.key.words': {
|
|
99
111
|
ru: 'Ctrl+← / Ctrl+→ перемещение по словам',
|
|
100
112
|
en: 'Ctrl+← / Ctrl+→ move by words',
|
|
@@ -140,6 +152,10 @@ const CATALOG = {
|
|
|
140
152
|
ru: '/resume <n> открыть чат №n из /chats',
|
|
141
153
|
en: '/resume <n> open chat #n from /chats',
|
|
142
154
|
},
|
|
155
|
+
'help.cmd.last': {
|
|
156
|
+
ru: '/last открыть последний чат этого каталога',
|
|
157
|
+
en: '/last open the last chat of this directory',
|
|
158
|
+
},
|
|
143
159
|
'help.cmd.chat': {
|
|
144
160
|
ru: '/chat показать текущий chat id',
|
|
145
161
|
en: '/chat show current chat id',
|
|
@@ -351,6 +367,10 @@ const CATALOG = {
|
|
|
351
367
|
ru: '/debug-dom сохранить HTML страницы (для отладки)',
|
|
352
368
|
en: '/debug-dom dump page HTML (debug)',
|
|
353
369
|
},
|
|
370
|
+
'help.no_topic': {
|
|
371
|
+
ru: 'Нет команды по запросу «{v}». Попробуй /help без аргумента.',
|
|
372
|
+
en: 'No command matches "{v}". Try /help without arguments.',
|
|
373
|
+
},
|
|
354
374
|
'help.cmd.help': {
|
|
355
375
|
ru: '/help, help справка',
|
|
356
376
|
en: '/help, help this help',
|
|
@@ -443,7 +463,15 @@ const CATALOG = {
|
|
|
443
463
|
'help.files.profile': { ru: 'Профиль:', en: 'Profile:' },
|
|
444
464
|
'help.files.snapshots': { ru: 'Снапшоты:', en: 'Snapshots:' },
|
|
445
465
|
'help.files.tmp': { ru: 'Временные:', en: 'Temp:' },
|
|
466
|
+
'help.files.tmp_note': {
|
|
467
|
+
ru: '(в .gitignore, очищается при старте)',
|
|
468
|
+
en: '(.gitignore, cleaned on start)',
|
|
469
|
+
},
|
|
446
470
|
'help.files.config': { ru: 'Конфиг:', en: 'Config:' },
|
|
471
|
+
'help.toggles_legend': {
|
|
472
|
+
ru: 'Индикаторы в статус-строке: 🧠 — глубокое размышление, 🌐 — поиск в интернете (видны, когда включены).',
|
|
473
|
+
en: 'Status-line icons: 🧠 — Deep thinking, 🌐 — web search (shown when enabled).',
|
|
474
|
+
},
|
|
447
475
|
// ---------- spinner ----------
|
|
448
476
|
'spinner.phrases': {
|
|
449
477
|
ru: 'Анализирую запрос…|Изучаю контекст…|Планирую решение…|Обрабатываю данные…|Формулирую ответ…|Проверяю детали…|Собираю информацию…|Обдумываю варианты…|Структурирую мысли…|Готовлю решение…|Анализирую код…|Уточняю детали…|Выполняю вычисления…|Систематизирую данные…|Прорабатываю задачу…|Проверяю логику…|Оптимизирую подход…|Формирую выводы…',
|
|
@@ -464,6 +492,15 @@ const CATALOG = {
|
|
|
464
492
|
'state.generating': { ru: '▶ генерация', en: '▶ generating' },
|
|
465
493
|
'editor.more': { ru: '…ещё {n}', en: '…{n} more' },
|
|
466
494
|
'editor.answer': { ru: '● Ответ', en: '● Answer' },
|
|
495
|
+
// Reverse search (Ctrl+R) status line. The query may be empty.
|
|
496
|
+
'editor.search_prompt': {
|
|
497
|
+
ru: '(обратный поиск) `{q}` — Ctrl+R дальше, Enter принять, Esc отмена',
|
|
498
|
+
en: '(reverse-i-search) `{q}` — Ctrl+R next, Enter accept, Esc cancel',
|
|
499
|
+
},
|
|
500
|
+
'editor.search_fail': {
|
|
501
|
+
ru: '(обратный поиск) `{q}` — не найдено, Esc отмена',
|
|
502
|
+
en: '(reverse-i-search) `{q}` — not found, Esc cancel',
|
|
503
|
+
},
|
|
467
504
|
// ---------- status ----------
|
|
468
505
|
'status.workdir': {
|
|
469
506
|
ru: 'Рабочая директория: {v}',
|
|
@@ -539,10 +576,15 @@ const CATALOG = {
|
|
|
539
576
|
en: 'Opening chat {id}...',
|
|
540
577
|
},
|
|
541
578
|
'msg.chat_opened': { ru: 'Чат открыт.', en: 'Chat opened.' },
|
|
579
|
+
'msg.version': { ru: 'Версия: {v}', en: 'Version: {v}' },
|
|
542
580
|
'msg.interactive': {
|
|
543
581
|
ru: 'Интерактивный режим. Введите задачу. Команды — /help. Выход — /exit.',
|
|
544
582
|
en: 'Interactive mode. Enter a task. Commands — /help. Exit — /exit.',
|
|
545
583
|
},
|
|
584
|
+
'msg.first_hint': {
|
|
585
|
+
ru: 'Подсказка: «/» — список команд, Ctrl+R — поиск по истории, картинку можно вставить через Ctrl+Shift+V.',
|
|
586
|
+
en: 'Hint: "/" lists commands, Ctrl+R searches history, paste an image with Ctrl+Shift+V.',
|
|
587
|
+
},
|
|
546
588
|
'msg.queue_hint': {
|
|
547
589
|
ru: 'Пока агент работает, можно печатать следующее сообщение — оно уйдёт в очередь (Enter — отправить, Esc — прервать).',
|
|
548
590
|
en: 'While the agent works you can type the next message — it goes to the queue (Enter — send, Esc — abort).',
|
|
@@ -601,6 +643,99 @@ const CATALOG = {
|
|
|
601
643
|
'dur.h': { ru: 'ч', en: 'h' },
|
|
602
644
|
'dur.m': { ru: 'м', en: 'm' },
|
|
603
645
|
'dur.s': { ru: 'с', en: 's' },
|
|
646
|
+
// ---------- /diff ----------
|
|
647
|
+
'diff.no_changes': { ru: '(нет изменений)', en: '(no changes)' },
|
|
648
|
+
'diff.more_lines': {
|
|
649
|
+
ru: '... [{n} строк ещё, полный вывод — Bash git diff]',
|
|
650
|
+
en: '... [{n} more lines, use Bash git diff for the full output]',
|
|
651
|
+
},
|
|
652
|
+
// ---------- /cost ----------
|
|
653
|
+
'cost.title': { ru: 'Статистика сессии:', en: 'Session stats:' },
|
|
654
|
+
'cost.context_known': {
|
|
655
|
+
ru: ' контекст: ~{n} токенов (DeepSeek accumulated_token_usage)',
|
|
656
|
+
en: ' context: ~{n} tokens (DeepSeek accumulated_token_usage)',
|
|
657
|
+
},
|
|
658
|
+
'cost.context_unknown': {
|
|
659
|
+
ru: ' контекст: неизвестно (DeepSeek сообщит после первого ответа в чате)',
|
|
660
|
+
en: ' context: unknown (DeepSeek reports it after the first answer in a chat)',
|
|
661
|
+
},
|
|
662
|
+
'cost.tasks': { ru: ' задач: {n}', en: ' tasks: {n}' },
|
|
663
|
+
'cost.tool_calls': {
|
|
664
|
+
ru: ' вызовов инструментов: {n}',
|
|
665
|
+
en: ' tool calls: {n}',
|
|
666
|
+
},
|
|
667
|
+
'cost.auto_compacts': { ru: ' авто-сжатий: {n}', en: ' auto-compacts: {n}' },
|
|
668
|
+
'cost.by_tool': { ru: ' по инструментам:', en: ' by tool:' },
|
|
669
|
+
'cost.duration': { ru: ' длительность: {dur}', en: ' duration: {dur}' },
|
|
670
|
+
'cost.started': { ru: ' начало: {v}', en: ' started: {v}' },
|
|
671
|
+
'cost.transcript': { ru: ' транскрипт: {v}', en: ' transcript: {v}' },
|
|
672
|
+
'cost.off': { ru: '(выкл)', en: '(off)' },
|
|
673
|
+
// ---------- /doctor ----------
|
|
674
|
+
'doctor.title': { ru: 'Диагностика:', en: 'Doctor:' },
|
|
675
|
+
'doctor.repo': { ru: 'репозиторий ({v})', en: 'repo ({v})' },
|
|
676
|
+
'doctor.detached': { ru: 'detached', en: 'detached' },
|
|
677
|
+
'doctor.not_repo': { ru: 'не репозиторий', en: 'not a repository' },
|
|
678
|
+
'doctor.loaded': { ru: 'загружен', en: 'loaded' },
|
|
679
|
+
'doctor.error': { ru: 'ошибка: {v}', en: 'error: {v}' },
|
|
680
|
+
'doctor.unknown': { ru: 'неизвестно', en: 'unknown' },
|
|
681
|
+
'doctor.bundled': { ru: 'встроенный chromium', en: 'bundled chromium' },
|
|
682
|
+
'doctor.auth_saved': {
|
|
683
|
+
ru: 'сессия сохранена (авто-вход готов)',
|
|
684
|
+
en: 'session saved (auto re-login ready)',
|
|
685
|
+
},
|
|
686
|
+
'doctor.auth_none': { ru: 'нет сохранённой сессии', en: 'no saved session' },
|
|
687
|
+
'doctor.clipboard_none': {
|
|
688
|
+
ru: 'инструмент не найден (вставка картинок отключена)',
|
|
689
|
+
en: 'no tool found (image paste disabled)',
|
|
690
|
+
},
|
|
691
|
+
'doctor.mcp': {
|
|
692
|
+
ru: '{servers} сервер(ов), {tools} инструмент(ов)',
|
|
693
|
+
en: '{servers} server(s), {tools} tool(s)',
|
|
694
|
+
},
|
|
695
|
+
'doctor.tokens': { ru: '{v} токенов', en: '{v} tokens' },
|
|
696
|
+
'doctor.origin_ok': { ru: 'origin настроен', en: 'origin configured' },
|
|
697
|
+
'doctor.origin_none': { ru: 'нет origin', en: 'no origin' },
|
|
698
|
+
'doctor.send_pause': {
|
|
699
|
+
ru: '{n}с между отправками агента',
|
|
700
|
+
en: '{n}s between agent sends',
|
|
701
|
+
},
|
|
702
|
+
'doctor.ssh': { ru: 'SSH-подключение', en: 'SSH remote' },
|
|
703
|
+
'doctor.local': { ru: 'локальный терминал', en: 'local terminal' },
|
|
704
|
+
'doctor.ua_cached': {
|
|
705
|
+
ru: 'UA закэширован (без перезапуска)',
|
|
706
|
+
en: 'UA cached (no relaunch)',
|
|
707
|
+
},
|
|
708
|
+
'doctor.ua_not_cached': {
|
|
709
|
+
ru: 'UA ещё не закэширован (первый запуск перезапустится один раз)',
|
|
710
|
+
en: 'no cached UA yet (first start relaunches once)',
|
|
711
|
+
},
|
|
712
|
+
// ---------- /permissions ----------
|
|
713
|
+
'perm.title': {
|
|
714
|
+
ru: 'Права инструментов (настройки подтверждений):',
|
|
715
|
+
en: 'Tool permissions (confirmation settings):',
|
|
716
|
+
},
|
|
717
|
+
'perm.write': { ru: ' Write: {v}', en: ' Write: {v}' },
|
|
718
|
+
'perm.edit': { ru: ' Edit: {v}', en: ' Edit: {v}' },
|
|
719
|
+
'perm.bash': { ru: ' Bash: {v}', en: ' Bash: {v}' },
|
|
720
|
+
'perm.ask': { ru: 'спрашивать', en: 'ask' },
|
|
721
|
+
'perm.allow': { ru: 'разрешить', en: 'allow' },
|
|
722
|
+
'perm.always': {
|
|
723
|
+
ru: ' Всегда подтверждать (regex):',
|
|
724
|
+
en: ' Always confirm (regex):',
|
|
725
|
+
},
|
|
726
|
+
'perm.change_hint': {
|
|
727
|
+
ru: 'Изменить: /config set confirmation.write false (и .edit / .bash)',
|
|
728
|
+
en: 'Change with: /config set confirmation.write false (and .edit / .bash)',
|
|
729
|
+
},
|
|
730
|
+
// ---------- /add-dir ----------
|
|
731
|
+
'adddir.usage': {
|
|
732
|
+
ru: 'Использование: /add-dir <path>',
|
|
733
|
+
en: 'Usage: /add-dir <path>',
|
|
734
|
+
},
|
|
735
|
+
'adddir.same': {
|
|
736
|
+
ru: 'Это уже рабочая директория.',
|
|
737
|
+
en: 'This is already the working directory.',
|
|
738
|
+
},
|
|
604
739
|
'msg.from_queue': { ru: '▶ Из очереди: ', en: '▶ From queue: ' },
|
|
605
740
|
'msg.from_queue_batch': {
|
|
606
741
|
ru: '▶ Из очереди ({n} сообщ.): ',
|
|
@@ -961,6 +1096,10 @@ const CATALOG = {
|
|
|
961
1096
|
},
|
|
962
1097
|
'chats.history_you': { ru: 'Вы', en: 'You' },
|
|
963
1098
|
'chats.history_agent': { ru: 'Агент', en: 'Agent' },
|
|
1099
|
+
'time.now': { ru: 'только что', en: 'just now' },
|
|
1100
|
+
'time.min_ago': { ru: '{n} мин назад', en: '{n}m ago' },
|
|
1101
|
+
'time.hour_ago': { ru: '{n} ч назад', en: '{n}h ago' },
|
|
1102
|
+
'time.day_ago': { ru: '{n} дн назад', en: '{n}d ago' },
|
|
964
1103
|
'sessions.dir': { ru: 'Папка сессий: {v}', en: 'Sessions dir: {v}' },
|
|
965
1104
|
'sessions.none': {
|
|
966
1105
|
ru: 'Сохранённых сессий нет. Они появятся после первой задачи/чата.',
|
|
@@ -1273,6 +1412,19 @@ const CATALOG = {
|
|
|
1273
1412
|
en: '✔ Saved: {v} = {value}',
|
|
1274
1413
|
},
|
|
1275
1414
|
'cfg.menu.default': { ru: 'по умолчанию', en: 'default' },
|
|
1415
|
+
'cfg.menu.filter_hint': {
|
|
1416
|
+
ru: '/ — поиск по настройкам, d — сбросить (дважды), * — изменено от дефолта',
|
|
1417
|
+
en: '/ search settings, d reset (press twice), * changed from default',
|
|
1418
|
+
},
|
|
1419
|
+
'cfg.menu.filter_active': {
|
|
1420
|
+
ru: 'Фильтр: «{q}» (Esc/Enter — очистить)',
|
|
1421
|
+
en: 'Filter: "{q}" (Esc/Enter to clear)',
|
|
1422
|
+
},
|
|
1423
|
+
'cfg.menu.no_match': { ru: '(нет совпадений)', en: '(no matches)' },
|
|
1424
|
+
'cfg.menu.reset_confirm': {
|
|
1425
|
+
ru: 'Нажми d ещё раз, чтобы сбросить {v}',
|
|
1426
|
+
en: 'Press d again to reset {v}',
|
|
1427
|
+
},
|
|
1276
1428
|
'cfg.menu.notty': {
|
|
1277
1429
|
ru: 'Меню доступно только в интерактивном терминале. Используй /config list|get|set.',
|
|
1278
1430
|
en: 'The menu needs an interactive terminal. Use /config list|get|set.',
|
package/dist/index.js
CHANGED
|
@@ -17,12 +17,12 @@ import { translate, normalizeLocale, localeDisplayName, isLocale, } from './i18n
|
|
|
17
17
|
import { Transcript } from './transcript.js';
|
|
18
18
|
import { UndoStore } from './undo.js';
|
|
19
19
|
import { selfReview, selfDiff, selfApply, selfList } from './self-review.js';
|
|
20
|
-
import { formatDiff, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, renderPermissions, resolveExtraDir, buildReviewPrompt, formatDuration, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, hasQueuedJob, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, isLiveConfigCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
|
|
20
|
+
import { formatDiff, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, renderPermissions, resolveExtraDir, buildReviewPrompt, formatDuration, formatRelativeTime, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, hasQueuedJob, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, isLiveConfigCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
|
|
21
21
|
import { performCompact } from './compact.js';
|
|
22
22
|
import { Scheduler, parseInterval, formatInterval, formatJobLine, parseCron, } from './scheduler.js';
|
|
23
23
|
import { renderMarkdown } from './markdown.js';
|
|
24
24
|
import { closeWeb } from './web.js';
|
|
25
|
-
import { saveSession, loadLastSession, readSession, listSessions, sessionsDir, } from './sessions.js';
|
|
25
|
+
import { saveSession, loadLastSession, readSession, listSessions, sessionsDir, loadHistory, saveHistory, } from './sessions.js';
|
|
26
26
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
27
|
const NL = String.fromCharCode(10);
|
|
28
28
|
// Floor for a /loop interval. A loop faster than the send throttle (15s)
|
|
@@ -292,10 +292,14 @@ ${theme.bold(t('help.options'))}
|
|
|
292
292
|
--version, -v ${t('help.opt.version')}
|
|
293
293
|
--help, -h ${t('help.opt.help')}
|
|
294
294
|
|
|
295
|
+
${t('help.color_note')}
|
|
296
|
+
|
|
295
297
|
${theme.bold(t('help.while_working'))}
|
|
296
298
|
${t('help.key.queue')}
|
|
297
299
|
${t('help.key.history')}
|
|
300
|
+
${t('help.key.search')}
|
|
298
301
|
${t('help.key.words')}
|
|
302
|
+
${t('help.key.undo')}
|
|
299
303
|
${t('help.key.slash')}
|
|
300
304
|
${t('help.key.newline')}
|
|
301
305
|
${t('help.key.backslash')}
|
|
@@ -308,6 +312,7 @@ ${theme.bold(t('help.commands'))}
|
|
|
308
312
|
${t('help.cmd.resume_id')}
|
|
309
313
|
${t('help.cmd.chats')}
|
|
310
314
|
${t('help.cmd.resume')}
|
|
315
|
+
${t('help.cmd.last')}
|
|
311
316
|
${t('help.cmd.chat')}
|
|
312
317
|
${t('help.cmd.cd')}
|
|
313
318
|
${t('help.cmd.pwd')}
|
|
@@ -316,20 +321,20 @@ ${theme.bold(t('help.commands'))}
|
|
|
316
321
|
${t('help.cmd.undo')}
|
|
317
322
|
${t('help.cmd.undo_list')}
|
|
318
323
|
${t('help.cmd.transcript')}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
324
|
+
${t('help.cmd.diff')}
|
|
325
|
+
${t('help.cmd.cost')}
|
|
326
|
+
${t('help.cmd.export')}
|
|
327
|
+
${t('help.cmd.doctor')}
|
|
328
|
+
${t('help.cmd.permissions')}
|
|
329
|
+
${t('help.cmd.add_dir')}
|
|
330
|
+
${t('help.cmd.review')}
|
|
331
|
+
${t('help.cmd.goal')}
|
|
332
|
+
${t('help.cmd.loop')}
|
|
333
|
+
${t('help.cmd.cron')}
|
|
334
|
+
${t('help.cmd.jobs')}
|
|
335
|
+
${t('help.cmd.thinking')}
|
|
336
|
+
${t('help.cmd.web')}
|
|
337
|
+
${t('help.cmd.compact')}
|
|
333
338
|
${t('help.cmd.queue')}
|
|
334
339
|
${t('help.cmd.tasks')}
|
|
335
340
|
${t('help.cmd.config')}
|
|
@@ -353,9 +358,11 @@ ${theme.bold(t('help.files'))}
|
|
|
353
358
|
${t('help.files.sessions')} ${sessionsDir()}
|
|
354
359
|
${t('help.files.profile')} ~/.zames/profile
|
|
355
360
|
${t('help.files.snapshots')} ~/.zames/snapshots
|
|
356
|
-
${t('help.files.tmp')} <project>/tmp (.
|
|
361
|
+
${t('help.files.tmp')} <project>/tmp ${t('help.files.tmp_note')}
|
|
357
362
|
${t('help.files.config')} ${CONFIG_PATHS.HOME_CONFIG}
|
|
358
363
|
${CONFIG_PATHS.PROJECT_CONFIG}
|
|
364
|
+
|
|
365
|
+
${theme.dim(t('help.toggles_legend'))}
|
|
359
366
|
`);
|
|
360
367
|
}
|
|
361
368
|
function dirLabel(p) {
|
|
@@ -372,6 +379,7 @@ const SLASH_COMMANDS = [
|
|
|
372
379
|
{ name: '/chats', key: 'help.cmd.chats' },
|
|
373
380
|
{ name: '/resume', key: 'help.cmd.resume' },
|
|
374
381
|
{ name: '/resume-id', key: 'help.cmd.resume_id' },
|
|
382
|
+
{ name: '/last', key: 'help.cmd.last' },
|
|
375
383
|
{ name: '/chat', key: 'help.cmd.chat' },
|
|
376
384
|
{ name: '/cd', key: 'help.cmd.cd' },
|
|
377
385
|
{ name: '/pwd', key: 'help.cmd.pwd' },
|
|
@@ -1190,9 +1198,14 @@ async function runTask(browser, tools, taskText, workdir, opts, attachments = []
|
|
|
1190
1198
|
}
|
|
1191
1199
|
// ---------- main ----------
|
|
1192
1200
|
async function main() {
|
|
1201
|
+
// Read the package version once: --version prints it and the interactive
|
|
1202
|
+
// banner shows it (a version in the first lines is what a bug report needs).
|
|
1203
|
+
const pkgVersion = await fs
|
|
1204
|
+
.readFile(path.join(__dirname, '..', 'package.json'), 'utf-8')
|
|
1205
|
+
.then((raw) => JSON.parse(raw).version)
|
|
1206
|
+
.catch(() => '');
|
|
1193
1207
|
if (hasFlag('--version') || hasFlag('-v')) {
|
|
1194
|
-
|
|
1195
|
-
console.log(pkg.version);
|
|
1208
|
+
console.log(pkgVersion || 'unknown');
|
|
1196
1209
|
return;
|
|
1197
1210
|
}
|
|
1198
1211
|
if (hasFlag('--help') || hasFlag('-h')) {
|
|
@@ -1215,6 +1228,7 @@ async function main() {
|
|
|
1215
1228
|
process.exit(1);
|
|
1216
1229
|
}
|
|
1217
1230
|
console.log(theme.system(t('msg.working_dir', { v: currentWorkdir })));
|
|
1231
|
+
console.log(theme.system(t('msg.version', { v: pkgVersion || '?' })));
|
|
1218
1232
|
const transcript = new Transcript({
|
|
1219
1233
|
dir: config.transcript.dir,
|
|
1220
1234
|
enabled: config.transcript.enabled,
|
|
@@ -1327,6 +1341,7 @@ async function main() {
|
|
|
1327
1341
|
}
|
|
1328
1342
|
console.log(theme.system(t('msg.interactive')));
|
|
1329
1343
|
console.log(theme.system(t('msg.queue_hint')));
|
|
1344
|
+
console.log(theme.dim(t('msg.first_hint')));
|
|
1330
1345
|
let freshChatNext = true;
|
|
1331
1346
|
let sendSystemPromptNext = true;
|
|
1332
1347
|
// Resuming an existing chat already has the system-prompt at its start, so
|
|
@@ -1532,6 +1547,11 @@ async function main() {
|
|
|
1532
1547
|
});
|
|
1533
1548
|
editor = ed;
|
|
1534
1549
|
ed.setTmpDir(TMP_DIR);
|
|
1550
|
+
// Restore the input history from the previous runs and persist every new
|
|
1551
|
+
// entry, so Ctrl+R / arrows can recall older messages after a restart.
|
|
1552
|
+
ed.history = loadHistory();
|
|
1553
|
+
ed._histIndex = ed.history.length;
|
|
1554
|
+
ed.onHistoryChange = (h) => saveHistory(h);
|
|
1535
1555
|
// The token context right-aligned on the status line (above the input).
|
|
1536
1556
|
// The editor pulls the number on every render, so it follows the live
|
|
1537
1557
|
// DeepSeek counter (accumulated_token_usage) without a polling timer.
|
|
@@ -1946,6 +1966,7 @@ async function main() {
|
|
|
1946
1966
|
fields: CONFIG_SCHEMA,
|
|
1947
1967
|
t,
|
|
1948
1968
|
get: (path) => getByPath(config, path),
|
|
1969
|
+
getDefault: (path) => getByPath(DEFAULTS, path),
|
|
1949
1970
|
set: (field, raw) => configSetRaw(field, raw),
|
|
1950
1971
|
reset: (field) => configResetField(field),
|
|
1951
1972
|
});
|
|
@@ -2102,8 +2123,29 @@ async function main() {
|
|
|
2102
2123
|
const lower = trimmed.toLowerCase();
|
|
2103
2124
|
if (['/exit', '/quit', 'exit', 'quit'].includes(lower))
|
|
2104
2125
|
break;
|
|
2105
|
-
if (lower === '/help' ||
|
|
2106
|
-
|
|
2126
|
+
if (lower === '/help' ||
|
|
2127
|
+
lower === 'help' ||
|
|
2128
|
+
lower.startsWith('/help ') ||
|
|
2129
|
+
lower.startsWith('help ')) {
|
|
2130
|
+
const topic = trimmed.replace(/^\/?help\s*/i, '').trim();
|
|
2131
|
+
if (!topic) {
|
|
2132
|
+
printHelp();
|
|
2133
|
+
}
|
|
2134
|
+
else {
|
|
2135
|
+
// /help <command> — print the full description of ONE command instead
|
|
2136
|
+
// of making the operator scroll the whole list.
|
|
2137
|
+
const q = '/' + topic.replace(/^\//, '').toLowerCase();
|
|
2138
|
+
const match = buildSlashCommands().filter((c) => c.name.toLowerCase() === q ||
|
|
2139
|
+
c.name.toLowerCase().startsWith(q + ' '));
|
|
2140
|
+
if (!match.length) {
|
|
2141
|
+
console.error(theme.warn(t('help.no_topic', { v: topic })));
|
|
2142
|
+
}
|
|
2143
|
+
else {
|
|
2144
|
+
for (const c of match) {
|
|
2145
|
+
console.log(theme.assistant(c.name) + theme.dim(' ' + c.description));
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2107
2149
|
continue;
|
|
2108
2150
|
}
|
|
2109
2151
|
if (['/new', '/clear', 'new'].includes(lower)) {
|
|
@@ -2401,12 +2443,47 @@ async function main() {
|
|
|
2401
2443
|
const mark = s.id === currentChatId ? theme.user(' *') : '';
|
|
2402
2444
|
const title = s.title ? ` ${s.title}` : '';
|
|
2403
2445
|
const wd = s.workdir ? theme.dim(` [${dirLabel(s.workdir)}]`) : '';
|
|
2404
|
-
|
|
2446
|
+
const age = formatRelativeTime(s.updatedAt, t);
|
|
2447
|
+
const ageText = age ? theme.dim(' ' + age) : '';
|
|
2448
|
+
console.log(` ${theme.user(n)}. ${s.id.slice(0, 8)}…${title}${wd}${ageText}${mark}`);
|
|
2405
2449
|
});
|
|
2406
2450
|
console.log(theme.system(t('sessions.restore_hint')));
|
|
2407
2451
|
}
|
|
2408
2452
|
continue;
|
|
2409
2453
|
}
|
|
2454
|
+
if (lower === '/last') {
|
|
2455
|
+
// Quick resume of the last chat for THIS working directory — otherwise
|
|
2456
|
+
// finding it meant /chats (a network fetch) or copying the id.
|
|
2457
|
+
const last = loadLastSession(currentWorkdir);
|
|
2458
|
+
if (!last || !last.id) {
|
|
2459
|
+
console.error(theme.error(t('sessions.none')));
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
if (editor)
|
|
2463
|
+
editor.lock(t('msg.input_locked'));
|
|
2464
|
+
try {
|
|
2465
|
+
if (editor)
|
|
2466
|
+
editor.clearScreen();
|
|
2467
|
+
console.log(theme.system(t('msg.opening_chat', { id: last.id })));
|
|
2468
|
+
await browser.openChat(last.id);
|
|
2469
|
+
currentChatId = last.id;
|
|
2470
|
+
freshChatNext = false;
|
|
2471
|
+
sendSystemPromptNext = promptOnResume();
|
|
2472
|
+
saveLastChat(last.id, currentWorkdir, last.title);
|
|
2473
|
+
transcript.log('resume_chat', { id: last.id, title: last.title });
|
|
2474
|
+
console.log(theme.assistant(t('msg.chat_opened')));
|
|
2475
|
+
loadSessionTodos(last.id);
|
|
2476
|
+
await printRestoredHistory(browser, editor, last.id);
|
|
2477
|
+
}
|
|
2478
|
+
catch (e) {
|
|
2479
|
+
console.error(theme.error(t('msg.open_chat_error', { v: '' })), e.message);
|
|
2480
|
+
}
|
|
2481
|
+
finally {
|
|
2482
|
+
if (editor)
|
|
2483
|
+
editor.unlock();
|
|
2484
|
+
}
|
|
2485
|
+
continue;
|
|
2486
|
+
}
|
|
2410
2487
|
if (lower.startsWith('/resume-id ')) {
|
|
2411
2488
|
const id = trimmed.slice('/resume-id'.length).trim();
|
|
2412
2489
|
if (!id) {
|
|
@@ -2818,7 +2895,7 @@ async function main() {
|
|
|
2818
2895
|
continue;
|
|
2819
2896
|
}
|
|
2820
2897
|
const out = await runGit(diffGitArgs(staged), currentWorkdir, 20_000);
|
|
2821
|
-
console.log(theme.system(formatDiff(out, { maxLines: 400 })));
|
|
2898
|
+
console.log(theme.system(formatDiff(out, { maxLines: 400 }, t)));
|
|
2822
2899
|
continue;
|
|
2823
2900
|
}
|
|
2824
2901
|
if (lower === '/cost' || lower === '/usage') {
|
|
@@ -2832,7 +2909,7 @@ async function main() {
|
|
|
2832
2909
|
// best-effort
|
|
2833
2910
|
}
|
|
2834
2911
|
}
|
|
2835
|
-
console.log(theme.system(renderCost(stats, transcript.file, browser.getLastTokenUsage())));
|
|
2912
|
+
console.log(theme.system(renderCost(stats, transcript.file, browser.getLastTokenUsage(), t)));
|
|
2836
2913
|
continue;
|
|
2837
2914
|
}
|
|
2838
2915
|
if (lower === '/export' || lower.startsWith('/export ')) {
|
|
@@ -2907,7 +2984,7 @@ async function main() {
|
|
|
2907
2984
|
sshRemote: !!(process.env.SSH_CONNECTION || process.env.SSH_TTY),
|
|
2908
2985
|
headless: !!headless,
|
|
2909
2986
|
headlessUaCached: await headlessUACacheReady().catch(() => false),
|
|
2910
|
-
})));
|
|
2987
|
+
}, t)));
|
|
2911
2988
|
continue;
|
|
2912
2989
|
}
|
|
2913
2990
|
if (lower === '/permissions' || lower === '/allowed-tools') {
|
|
@@ -2916,12 +2993,12 @@ async function main() {
|
|
|
2916
2993
|
edit: config.confirmation.edit,
|
|
2917
2994
|
bash: config.confirmation.bash,
|
|
2918
2995
|
alwaysConfirm: config.confirmation.alwaysConfirm,
|
|
2919
|
-
})));
|
|
2996
|
+
}, t)));
|
|
2920
2997
|
continue;
|
|
2921
2998
|
}
|
|
2922
2999
|
if (lower === '/add-dir' || lower.startsWith('/add-dir ')) {
|
|
2923
3000
|
const arg = trimmed.slice('/add-dir'.length).trim();
|
|
2924
|
-
const res = resolveExtraDir(arg, currentWorkdir);
|
|
3001
|
+
const res = resolveExtraDir(arg, currentWorkdir, t);
|
|
2925
3002
|
if ('error' in res) {
|
|
2926
3003
|
console.error(theme.warn(res.error));
|
|
2927
3004
|
continue;
|