zames_pro 2.5.1 → 2.6.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/dist/input.js CHANGED
@@ -8,8 +8,10 @@ import { randomThinkingPhrase, stripEllipsis } from './spinner.js';
8
8
  // * спиннер/статус и ответы печатаются ВЫШЕ строки ввода (printAbove);
9
9
  // * перерисовка учитывает перенос по ширине терминала, поэтому нет
10
10
  // дублирования строк при многострочном вводе;
11
- // * многострочный ввод: Enter отправляет, Ctrl+J (и Shift+Enter в
12
- // терминалах с расширенным протоколом) вставляют перевод строки;
11
+ // * многострочный ввод: Enter отправляет, но если курсор стоит сразу
12
+ // после «\», Enter удаляет «\» и переносит строку (как в Claude Code).
13
+ // Ctrl+J, Ctrl+Enter и Shift+Enter (в терминалах с расширенным
14
+ // протоколом) всегда вставляют перевод строки;
13
15
  // * перенос длинных строк по словам (слово не рвётся посередине);
14
16
  // * перемещение по словам: Ctrl+←/→ (и Alt+B/Alt+F), удаление слова
15
17
  // Ctrl+W; Ctrl+K — удалить до конца строки;
@@ -234,10 +236,38 @@ export class LineEditor {
234
236
  if (stdin.setRawMode)
235
237
  stdin.setRawMode(this._wasRaw || false);
236
238
  }
239
+ // Временно отдать терминал внешнему UI (например, меню /config):
240
+ // снимаем свой обработчик ввода и стираем блок ввода, чтобы чужой вывод
241
+ // не накладывался на строку ввода. Парно с resume().
242
+ pause() {
243
+ const stdin = process.stdin;
244
+ this._stopDots();
245
+ if (this.rendered)
246
+ this._eraseBlock();
247
+ stdin.removeListener('data', this._onData);
248
+ process.stdout.write(ESC + '[?2004l');
249
+ }
250
+ resume() {
251
+ const stdin = process.stdin;
252
+ if (!stdin.isTTY || !stdin.setRawMode)
253
+ return;
254
+ this._wasRaw = stdin.isRaw;
255
+ stdin.setRawMode(true);
256
+ stdin.resume();
257
+ process.stdout.write(ESC + '[?2004h');
258
+ stdin.on('data', this._onData);
259
+ this._writeBlock();
260
+ this._render();
261
+ }
237
262
  setPrompt(str) {
238
263
  this.promptStr = str;
239
264
  this._render();
240
265
  }
266
+ // Обновить описания slash-команд (смена языка интерфейса).
267
+ setCommands(commands) {
268
+ this.slashCommands = commands;
269
+ this._render();
270
+ }
241
271
  clear() {
242
272
  this.buf = '';
243
273
  this.cursor = 0;
@@ -398,6 +428,18 @@ export class LineEditor {
398
428
  this.buf = next.join('');
399
429
  this.cursor += ins.length;
400
430
  }
431
+ // Вставить перевод строки. Если курсор стоит сразу после символа «\»
432
+ // (как в Claude Code), символ удаляется — чтобы «\» + Enter давали
433
+ // обычный перенос без лишней обратной косой черты в тексте.
434
+ _insertNewline() {
435
+ const chars = Array.from(this.buf);
436
+ if (this.cursor > 0 && chars[this.cursor - 1] === '\\') {
437
+ chars.splice(this.cursor - 1, 1);
438
+ this.buf = chars.join('');
439
+ this.cursor--;
440
+ }
441
+ this._insert(NL);
442
+ }
401
443
  _backspace() {
402
444
  if (this.cursor <= 0)
403
445
  return;
@@ -570,21 +612,34 @@ export class LineEditor {
570
612
  }
571
613
  continue;
572
614
  }
573
- // Shift+Enter в терминалах с расширенным протоколом и Alt+Enter.
615
+ // Shift+Enter, Ctrl+Enter в терминалах с расширенным протоколом
616
+ // и Alt+Enter — вставка перевода строки.
574
617
  if (s.startsWith(ESC + '[13;2u')) {
575
- this._insert(NL);
618
+ this._insertNewline();
619
+ s = s.slice(7);
620
+ this._render();
621
+ continue;
622
+ }
623
+ if (s.startsWith(ESC + '[13;5u')) {
624
+ this._insertNewline();
576
625
  s = s.slice(7);
577
626
  this._render();
578
627
  continue;
579
628
  }
580
629
  if (s.startsWith(ESC + '[27;2;13~')) {
581
- this._insert(NL);
630
+ this._insertNewline();
631
+ s = s.slice(10);
632
+ this._render();
633
+ continue;
634
+ }
635
+ if (s.startsWith(ESC + '[27;5;13~')) {
636
+ this._insertNewline();
582
637
  s = s.slice(10);
583
638
  this._render();
584
639
  continue;
585
640
  }
586
641
  if (s.startsWith(ESC + NL)) {
587
- this._insert(NL);
642
+ this._insertNewline();
588
643
  s = s.slice(2);
589
644
  this._render();
590
645
  continue;
@@ -592,12 +647,22 @@ export class LineEditor {
592
647
  const ch = s[0];
593
648
  const code = s.charCodeAt(0);
594
649
  s = s.slice(1);
650
+ // Enter: если предыдущий символ — «\», Claude-Code-стиль: удаляем
651
+ // «\» и переносим строку; иначе отправляем сообщение.
652
+ // Ctrl+J (code 10) всегда вставляет перевод строки; Ctrl+Enter
653
+ // (ESC[13;5u) и Shift+Enter тоже.
595
654
  if (ch === CR) {
655
+ const before = this.cursor > 0 ? Array.from(this.buf)[this.cursor - 1] : '';
656
+ if (before === '\\') {
657
+ this._insertNewline();
658
+ this._render();
659
+ continue;
660
+ }
596
661
  this._submit();
597
662
  continue;
598
663
  }
599
664
  if (code === 10) {
600
- this._insert(NL);
665
+ this._insertNewline();
601
666
  this._render();
602
667
  continue;
603
668
  }
package/dist/spinner.js CHANGED
@@ -1,47 +1,21 @@
1
1
  import ora from 'ora';
2
2
  import { theme } from './theme.js';
3
3
  import { renderMarkdown } from './markdown.js';
4
+ import { translate } from './i18n.js';
4
5
  // Фразы для спиннера «агент работает». Выбираются в случайном порядке.
5
- const THINKING_PHRASES = [
6
- 'Замешиваю кал…',
7
- 'Взбиваю кал до однородной массы…',
8
- 'Мешаю кал с логикой…',
9
- 'Взбалтываю кал в коктейль…',
10
- 'Кручу венчиком по калу…',
11
- 'Смешиваю кал с кофеином…',
12
- 'Замешиваю тесто из кала…',
13
- 'Взбиваю пену из кала…',
14
- 'Перемешиваю кал лопатой…',
15
- 'Замешиваю глину из кала…',
16
- 'Гомогенизирую кал до состояния бетона…',
17
- 'Взбалтываю кал до просветления…',
18
- 'Блендерю кал в смузи…',
19
- 'Замешиваю цемент из кала…',
20
- 'Взбиваю кашу из кала…',
21
- 'Мешаю кал с надеждой…',
22
- 'Взбалтываю осадок из кала…',
23
- 'Кручу блендером по калу…',
24
- 'Смешиваю кал до неразличимости…',
25
- 'Замешиваю раствор из кала…',
26
- 'Взбиваю коктейль «кал»…',
27
- 'Перемешиваю кал лопатой дедлайна…',
28
- 'Замешиваю кашу из кала…',
29
- 'Взбиваю пенку из кала…',
30
- 'Гомогенизирую кал до однородности…',
31
- 'Взбалтываю кал в бетономешалке…',
32
- 'Замешиваю кал в тесто…',
33
- 'Сбиваю кал в однородную массу…',
34
- 'Размешиваю кал до просветления…',
35
- 'Замешиваю кал из всего подряд…',
36
- ];
37
- export function randomThinkingPhrase() {
38
- return THINKING_PHRASES[Math.floor(Math.random() * THINKING_PHRASES.length)];
6
+ // Текст берётся из i18n по ключу 'spinner.phrases' (строки разделены «|»).
7
+ export function randomThinkingPhrase(locale = 'ru') {
8
+ const raw = translate(locale)('spinner.phrases');
9
+ const phrases = raw.split('|').filter((s) => s.trim().length);
10
+ if (!phrases.length)
11
+ return '';
12
+ return phrases[Math.floor(Math.random() * phrases.length)];
39
13
  }
40
14
  // Убираем завершающее многоточие из фразы — точки анимируем отдельно.
41
15
  export function stripEllipsis(phrase) {
42
16
  return phrase.replace(/[.…]+\s*$/, '');
43
17
  }
44
- export function createSpinner() {
18
+ export function createSpinner(locale = 'ru') {
45
19
  let spinner = null;
46
20
  let dotTimer = null;
47
21
  let dotPhase = 0;
@@ -66,10 +40,10 @@ export function createSpinner() {
66
40
  };
67
41
  // Подсказка в строке статуса: во время работы агента терминал живой,
68
42
  // можно печатать следующее сообщение. Без неё это неочевидно.
69
- const HINT = theme.dim(' · Esc — стоп');
43
+ const HINT = theme.dim(' · ' + translate(locale)('spinner.hint'));
70
44
  // Запуск анимированного статуса: коричневый текст + «бегущие» точки.
71
45
  const startThinking = () => {
72
- const base = theme.brown(stripEllipsis(randomThinkingPhrase()));
46
+ const base = theme.brown(stripEllipsis(randomThinkingPhrase(locale)));
73
47
  // Точки того же цвета, что и база, и фиксированной ширины — иначе
74
48
  // подсказка справа «прыгает» при смене фазы анимации.
75
49
  const dots = (n) => theme.brown(DOTS[n] + DOTS_PAD.slice(DOTS[n].length));
@@ -1,6 +1,8 @@
1
- export function buildSystemPrompt({ workdir, tools, gitContext = null, }) {
1
+ import { translate } from './i18n.js';
2
+ export function buildSystemPrompt({ workdir, tools, gitContext = null, locale = 'ru', }) {
3
+ const t = translate(locale);
2
4
  const toolDescriptions = tools
3
- .map((t) => `### ${t.name}\n${t.description}\nПараметры: ${JSON.stringify(t.parameters)}`)
5
+ .map((t2) => `### ${t2.name}\n${t2.description}\nПараметры: ${JSON.stringify(t2.parameters)}`)
4
6
  .join('\n\n');
5
7
  const gitSection = gitContext
6
8
  ? `\n## Git context\n\n${gitContext}\n`
@@ -9,6 +11,10 @@ export function buildSystemPrompt({ workdir, tools, gitContext = null, }) {
9
11
 
10
12
  You work in the directory: ${workdir}
11
13
 
14
+ ## LANGUAGE
15
+
16
+ ${t('prompt.answer_language')}
17
+
12
18
  ## SILENT OPERATION (most important rule)
13
19
 
14
20
  Work silently. While you are still solving the task you must output ONLY
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {