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/README.md +32 -0
- package/dist/agent-loop.js +145 -6
- package/dist/browser.js +1 -1
- package/dist/config-menu.js +231 -0
- package/dist/config.js +143 -0
- package/dist/i18n.js +276 -0
- package/dist/index.js +415 -189
- package/dist/input.js +72 -7
- package/dist/spinner.js +11 -37
- package/dist/system-prompt.js +8 -2
- package/package.json +1 -1
package/dist/input.js
CHANGED
|
@@ -8,8 +8,10 @@ import { randomThinkingPhrase, stripEllipsis } from './spinner.js';
|
|
|
8
8
|
// * спиннер/статус и ответы печатаются ВЫШЕ строки ввода (printAbove);
|
|
9
9
|
// * перерисовка учитывает перенос по ширине терминала, поэтому нет
|
|
10
10
|
// дублирования строк при многострочном вводе;
|
|
11
|
-
// * многострочный ввод: 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 в терминалах с расширенным протоколом
|
|
615
|
+
// Shift+Enter, Ctrl+Enter в терминалах с расширенным протоколом
|
|
616
|
+
// и Alt+Enter — вставка перевода строки.
|
|
574
617
|
if (s.startsWith(ESC + '[13;2u')) {
|
|
575
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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(' ·
|
|
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));
|
package/dist/system-prompt.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
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((
|
|
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
|