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/index.js
CHANGED
|
@@ -8,7 +8,9 @@ import { createTools } from './tools.js';
|
|
|
8
8
|
import { runAgentLoop } from './agent-loop.js';
|
|
9
9
|
import { createSpinner } from './spinner.js';
|
|
10
10
|
import { LineEditor } from './input.js';
|
|
11
|
-
import { loadConfig, CONFIG_PATHS, ZAMES_HOME } from './config.js';
|
|
11
|
+
import { loadConfig, DEFAULTS, CONFIG_PATHS, ZAMES_HOME, CONFIG_SCHEMA, getByPath, validateConfigValue, writeConfigValue, resetConfigValue, } from './config.js';
|
|
12
|
+
import { runConfigMenu } from './config-menu.js';
|
|
13
|
+
import { translate, normalizeLocale, localeDisplayName, isLocale, } from './i18n.js';
|
|
12
14
|
import { Transcript } from './transcript.js';
|
|
13
15
|
import { UndoStore } from './undo.js';
|
|
14
16
|
import { selfReview, selfDiff, selfApply, selfList } from './self-review.js';
|
|
@@ -39,6 +41,13 @@ function getPositional() {
|
|
|
39
41
|
return positional;
|
|
40
42
|
}
|
|
41
43
|
const config = loadConfig();
|
|
44
|
+
// Текущий язык интерфейса/агента. Меняется командой /config lang <ru|en>.
|
|
45
|
+
let currentLocale = isLocale(config.ui?.locale)
|
|
46
|
+
? config.ui.locale
|
|
47
|
+
: 'ru';
|
|
48
|
+
// Перевод: читает currentLocale в момент вызова, поэтому смена языка
|
|
49
|
+
// действует сразу, без перезапуска (для уже напечатанных строк).
|
|
50
|
+
const t = (key, params) => translate(currentLocale)(key, params);
|
|
42
51
|
const headless = hasFlag('--headless') || config.headless;
|
|
43
52
|
const debug = hasFlag('--debug') || config.debug;
|
|
44
53
|
const calibrate = hasFlag('--calibrate');
|
|
@@ -154,7 +163,7 @@ async function autoReload() {
|
|
|
154
163
|
return;
|
|
155
164
|
const { errors } = await reloadModules();
|
|
156
165
|
if (errors.length) {
|
|
157
|
-
console.error(theme.warn('
|
|
166
|
+
console.error(theme.warn(t('reload.auto_partial')));
|
|
158
167
|
for (const e of errors)
|
|
159
168
|
console.error(theme.warn(' ' + e));
|
|
160
169
|
}
|
|
@@ -162,68 +171,68 @@ async function autoReload() {
|
|
|
162
171
|
// ---------- helpers ----------
|
|
163
172
|
function printHelp() {
|
|
164
173
|
console.log(`
|
|
165
|
-
${theme.bold('zames')} —
|
|
174
|
+
${theme.bold('zames')} — ${t('app.tagline')}
|
|
166
175
|
|
|
167
|
-
${theme.bold('
|
|
168
|
-
--dir <path>
|
|
169
|
-
--task <text>
|
|
170
|
-
--chat <id>
|
|
171
|
-
--resume-last
|
|
172
|
-
--new-chat
|
|
173
|
-
--resend-prompt
|
|
174
|
-
--max-iter <n>
|
|
175
|
-
--headless
|
|
176
|
-
--debug
|
|
177
|
-
--calibrate
|
|
178
|
-
--dev
|
|
179
|
-
--version, -v
|
|
180
|
-
--help, -h
|
|
176
|
+
${theme.bold(t('help.options'))}
|
|
177
|
+
--dir <path> ${t('help.opt.dir')}
|
|
178
|
+
--task <text> ${t('help.opt.task')}
|
|
179
|
+
--chat <id> ${t('help.opt.chat')}
|
|
180
|
+
--resume-last ${t('help.opt.resume_last')}
|
|
181
|
+
--new-chat ${t('help.opt.new_chat')}
|
|
182
|
+
--resend-prompt ${t('help.opt.resend_prompt')}
|
|
183
|
+
--max-iter <n> ${t('help.opt.max_iter', { n: config.maxIterations })}
|
|
184
|
+
--headless ${t('help.opt.headless')}
|
|
185
|
+
--debug ${t('help.opt.debug')}
|
|
186
|
+
--calibrate ${t('help.opt.calibrate')}
|
|
187
|
+
--dev ${t('help.opt.dev')}
|
|
188
|
+
--version, -v ${t('help.opt.version')}
|
|
189
|
+
--help, -h ${t('help.opt.help')}
|
|
181
190
|
|
|
182
|
-
${theme.bold('
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
191
|
+
${theme.bold(t('help.while_working'))}
|
|
192
|
+
${t('help.key.queue')}
|
|
193
|
+
${t('help.key.history')}
|
|
194
|
+
${t('help.key.words')}
|
|
195
|
+
${t('help.key.slash')}
|
|
196
|
+
${t('help.key.newline')}
|
|
197
|
+
${t('help.key.backslash')}
|
|
198
|
+
${t('help.key.esc')}
|
|
189
199
|
|
|
190
|
-
${theme.bold('
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
200
|
+
${theme.bold(t('help.commands'))}
|
|
201
|
+
${t('help.cmd.new')}
|
|
202
|
+
${t('help.cmd.sessions')}
|
|
203
|
+
${t('help.cmd.resume_id')}
|
|
204
|
+
${t('help.cmd.chats')}
|
|
205
|
+
${t('help.cmd.resume')}
|
|
206
|
+
${t('help.cmd.chat')}
|
|
207
|
+
${t('help.cmd.cd')}
|
|
208
|
+
${t('help.cmd.pwd')}
|
|
209
|
+
${t('help.cmd.status')}
|
|
210
|
+
${t('help.cmd.reload')}
|
|
211
|
+
${t('help.cmd.undo')}
|
|
212
|
+
${t('help.cmd.undo_list')}
|
|
213
|
+
${t('help.cmd.transcript')}
|
|
214
|
+
${t('help.cmd.config')}
|
|
215
|
+
${t('help.cmd.lang')}
|
|
216
|
+
${t('help.cmd.debug_dom')}
|
|
217
|
+
${t('help.cmd.help')}
|
|
218
|
+
${t('help.cmd.exit')}
|
|
208
219
|
|
|
209
|
-
${theme.bold(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
/self-diff <name> различия между текущим src/ и снапшотом
|
|
217
|
-
/self-apply <name> применить снапшот к src/ (с бэкапом)
|
|
220
|
+
${theme.bold(t('help.self_review'))}
|
|
221
|
+
${t('help.self.review')}
|
|
222
|
+
${t('help.self.fix')}
|
|
223
|
+
${t('help.self.done')}
|
|
224
|
+
${t('help.self.list')}
|
|
225
|
+
${t('help.self.diff')}
|
|
226
|
+
${t('help.self.apply')}
|
|
218
227
|
|
|
219
|
-
${theme.bold('
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
${theme.bold(t('help.files'))}
|
|
229
|
+
${t('help.files.logs')} ${config.transcript.dir}
|
|
230
|
+
${t('help.files.undo')} ~/.zames/undo
|
|
231
|
+
${t('help.files.sessions')} ${sessionsDir()}
|
|
232
|
+
${t('help.files.profile')} ~/.zames/profile
|
|
233
|
+
${t('help.files.snapshots')} ~/.zames/snapshots
|
|
234
|
+
${t('help.files.tmp')} <project>/tmp (.gitignore, cleaned on start)
|
|
235
|
+
${t('help.files.config')} ${CONFIG_PATHS.HOME_CONFIG}
|
|
227
236
|
${CONFIG_PATHS.PROJECT_CONFIG}
|
|
228
237
|
`);
|
|
229
238
|
}
|
|
@@ -231,33 +240,42 @@ function dirLabel(p) {
|
|
|
231
240
|
return path.basename(p) || p;
|
|
232
241
|
}
|
|
233
242
|
// Список slash-команд для автодополнения при вводе «/» (Tab — дополнить).
|
|
243
|
+
// Описание локализуется по ключу help.cmd.* в момент отображения — см.
|
|
244
|
+
// buildSlashCommands().
|
|
234
245
|
const SLASH_COMMANDS = [
|
|
235
|
-
{ name: '/help',
|
|
236
|
-
{ name: '/new',
|
|
237
|
-
{ name: '/clear',
|
|
238
|
-
{ name: '/sessions',
|
|
239
|
-
{ name: '/chats',
|
|
240
|
-
{ name: '/resume',
|
|
241
|
-
{ name: '/resume-id',
|
|
242
|
-
{ name: '/chat',
|
|
243
|
-
{ name: '/cd',
|
|
244
|
-
{ name: '/pwd',
|
|
245
|
-
{ name: '/status',
|
|
246
|
-
{ name: '/reload',
|
|
247
|
-
{ name: '/undo',
|
|
248
|
-
{ name: '/undo-list',
|
|
249
|
-
{ name: '/transcript',
|
|
250
|
-
{ name: '/config',
|
|
251
|
-
{ name: '/debug-dom',
|
|
252
|
-
{ name: '/self-review',
|
|
253
|
-
{ name: '/self-fix',
|
|
254
|
-
{ name: '/self-done',
|
|
255
|
-
{ name: '/self-list',
|
|
256
|
-
{ name: '/self-diff',
|
|
257
|
-
{ name: '/self-apply',
|
|
258
|
-
{ name: '/exit',
|
|
259
|
-
{ name: '/quit',
|
|
246
|
+
{ name: '/help', key: 'help.cmd.help' },
|
|
247
|
+
{ name: '/new', key: 'help.cmd.new' },
|
|
248
|
+
{ name: '/clear', key: 'help.cmd.new' },
|
|
249
|
+
{ name: '/sessions', key: 'help.cmd.sessions' },
|
|
250
|
+
{ name: '/chats', key: 'help.cmd.chats' },
|
|
251
|
+
{ name: '/resume', key: 'help.cmd.resume' },
|
|
252
|
+
{ name: '/resume-id', key: 'help.cmd.resume_id' },
|
|
253
|
+
{ name: '/chat', key: 'help.cmd.chat' },
|
|
254
|
+
{ name: '/cd', key: 'help.cmd.cd' },
|
|
255
|
+
{ name: '/pwd', key: 'help.cmd.pwd' },
|
|
256
|
+
{ name: '/status', key: 'help.cmd.status' },
|
|
257
|
+
{ name: '/reload', key: 'help.cmd.reload' },
|
|
258
|
+
{ name: '/undo', key: 'help.cmd.undo' },
|
|
259
|
+
{ name: '/undo-list', key: 'help.cmd.undo_list' },
|
|
260
|
+
{ name: '/transcript', key: 'help.cmd.transcript' },
|
|
261
|
+
{ name: '/config', key: 'help.cmd.config' },
|
|
262
|
+
{ name: '/debug-dom', key: 'help.cmd.debug_dom' },
|
|
263
|
+
{ name: '/self-review', key: 'help.self.review' },
|
|
264
|
+
{ name: '/self-fix', key: 'help.self.fix' },
|
|
265
|
+
{ name: '/self-done', key: 'help.self.done' },
|
|
266
|
+
{ name: '/self-list', key: 'help.self.list' },
|
|
267
|
+
{ name: '/self-diff', key: 'help.self.diff' },
|
|
268
|
+
{ name: '/self-apply', key: 'help.self.apply' },
|
|
269
|
+
{ name: '/exit', key: 'help.cmd.exit' },
|
|
270
|
+
{ name: '/quit', key: 'help.cmd.exit' },
|
|
260
271
|
];
|
|
272
|
+
// Описания slash-команд на текущем языке (для подсказок LineEditor).
|
|
273
|
+
function buildSlashCommands() {
|
|
274
|
+
return SLASH_COMMANDS.map((c) => ({
|
|
275
|
+
name: c.name,
|
|
276
|
+
description: t(c.key),
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
261
279
|
// Временные файлы агента (одноразовые скрипты и т.п.) складываем в
|
|
262
280
|
// <проект>/tmp — эта папка в .gitignore и очищается при каждом запуске.
|
|
263
281
|
const TMP_DIR = path.join(__dirname, '..', 'tmp');
|
|
@@ -629,13 +647,13 @@ async function runTask(browser, tools, taskText, workdir, opts) {
|
|
|
629
647
|
// Новая задача с промпта — сбрасываем «стоп» от прошлого прерывания.
|
|
630
648
|
browser._stopped = false;
|
|
631
649
|
browser._abort = false;
|
|
632
|
-
const ui = editor || mod.createSpinner();
|
|
650
|
+
const ui = editor || mod.createSpinner(currentLocale);
|
|
633
651
|
const stopWatching = editor
|
|
634
652
|
? () => { }
|
|
635
653
|
: watchInput({
|
|
636
654
|
onEscape: () => {
|
|
637
655
|
ui.stop();
|
|
638
|
-
console.error(theme.warn('
|
|
656
|
+
console.error(theme.warn(t('msg.abort_gen_short')));
|
|
639
657
|
browser.stopGeneration().catch(() => { });
|
|
640
658
|
},
|
|
641
659
|
onChange: (text) => ui.setPending(text),
|
|
@@ -643,7 +661,7 @@ async function runTask(browser, tools, taskText, workdir, opts) {
|
|
|
643
661
|
queue.push(text);
|
|
644
662
|
ui.setPending(null);
|
|
645
663
|
ui.stop();
|
|
646
|
-
console.log(theme.user('
|
|
664
|
+
console.log(theme.user(t('msg.queued', { n: queue.length })) +
|
|
647
665
|
theme.assistant(text));
|
|
648
666
|
ui.thinking();
|
|
649
667
|
},
|
|
@@ -669,6 +687,7 @@ async function runTask(browser, tools, taskText, workdir, opts) {
|
|
|
669
687
|
onAssistantMessage: (msg) => ui.assistant(msg),
|
|
670
688
|
onChatReady,
|
|
671
689
|
debugLog: debug,
|
|
690
|
+
locale: currentLocale,
|
|
672
691
|
});
|
|
673
692
|
// Прервали (Esc/Ctrl+C) — не запускаем следующие задачи из очереди
|
|
674
693
|
// и очищаем её, чтобы «стоп» действительно останавливал всё.
|
|
@@ -681,10 +700,10 @@ async function runTask(browser, tools, taskText, workdir, opts) {
|
|
|
681
700
|
const queued = queue.shift() ?? '';
|
|
682
701
|
ui.stop();
|
|
683
702
|
if (editor) {
|
|
684
|
-
editor.printAbove(theme.user('
|
|
703
|
+
editor.printAbove(theme.user(t('msg.from_queue')) + theme.assistant(queued));
|
|
685
704
|
}
|
|
686
705
|
else {
|
|
687
|
-
console.log(theme.user('
|
|
706
|
+
console.log(theme.user(t('msg.from_queue')) + theme.assistant(queued));
|
|
688
707
|
}
|
|
689
708
|
transcript?.log('queued_task', { task: queued });
|
|
690
709
|
next = { task: queued, freshChat: false, sendSystemPrompt: false };
|
|
@@ -692,7 +711,7 @@ async function runTask(browser, tools, taskText, workdir, opts) {
|
|
|
692
711
|
}
|
|
693
712
|
catch (e) {
|
|
694
713
|
ui.stop();
|
|
695
|
-
console.error(theme.error(String.fromCharCode(10) + '
|
|
714
|
+
console.error(theme.error(String.fromCharCode(10) + t('msg.agent_error')), e.message);
|
|
696
715
|
if (debug)
|
|
697
716
|
console.error(e.stack);
|
|
698
717
|
transcript?.log('agent_error', { error: e.message });
|
|
@@ -714,7 +733,7 @@ async function main() {
|
|
|
714
733
|
return;
|
|
715
734
|
}
|
|
716
735
|
if (calibrate) {
|
|
717
|
-
console.log(theme.warn('
|
|
736
|
+
console.log(theme.warn(t('calibrate')));
|
|
718
737
|
}
|
|
719
738
|
await cleanTmpDir();
|
|
720
739
|
let currentWorkdir;
|
|
@@ -725,17 +744,17 @@ async function main() {
|
|
|
725
744
|
sandboxRoot = currentWorkdir;
|
|
726
745
|
}
|
|
727
746
|
catch (e) {
|
|
728
|
-
console.error(theme.error('
|
|
747
|
+
console.error(theme.error(t('msg.workdir_error')), e.message);
|
|
729
748
|
process.exit(1);
|
|
730
749
|
}
|
|
731
|
-
console.log(theme.system(
|
|
750
|
+
console.log(theme.system(t('msg.working_dir', { v: currentWorkdir })));
|
|
732
751
|
const transcript = new Transcript({
|
|
733
752
|
dir: config.transcript.dir,
|
|
734
753
|
enabled: config.transcript.enabled,
|
|
735
754
|
sessionName: dirLabel(currentWorkdir),
|
|
736
755
|
});
|
|
737
756
|
if (transcript.file) {
|
|
738
|
-
console.log(theme.system(
|
|
757
|
+
console.log(theme.system(t('msg.transcript', { v: transcript.file })));
|
|
739
758
|
}
|
|
740
759
|
const undo = new UndoStore(config.undo);
|
|
741
760
|
const browser = new DeepSeekBrowser({
|
|
@@ -744,7 +763,7 @@ async function main() {
|
|
|
744
763
|
channel: config.browserChannel,
|
|
745
764
|
...config.browser,
|
|
746
765
|
});
|
|
747
|
-
const bootSpinner = mod.createSpinner();
|
|
766
|
+
const bootSpinner = mod.createSpinner(currentLocale);
|
|
748
767
|
bootSpinner.thinking();
|
|
749
768
|
try {
|
|
750
769
|
await browser.launch();
|
|
@@ -753,7 +772,7 @@ async function main() {
|
|
|
753
772
|
}
|
|
754
773
|
catch (e) {
|
|
755
774
|
bootSpinner.stop();
|
|
756
|
-
console.error(theme.error('
|
|
775
|
+
console.error(theme.error(t('msg.browser_error')), e.message);
|
|
757
776
|
if (debug)
|
|
758
777
|
console.error(e.stack);
|
|
759
778
|
await browser.close().catch(() => { });
|
|
@@ -772,19 +791,19 @@ async function main() {
|
|
|
772
791
|
const last = loadLastSession(currentWorkdir);
|
|
773
792
|
if (last && last.id) {
|
|
774
793
|
resumeId = last.id;
|
|
775
|
-
console.log(theme.system(
|
|
794
|
+
console.log(theme.system(t('msg.resuming', { id: resumeId })));
|
|
776
795
|
}
|
|
777
796
|
}
|
|
778
797
|
if (resumeId) {
|
|
779
798
|
try {
|
|
780
|
-
console.log(theme.system(
|
|
799
|
+
console.log(theme.system(t('msg.opening_chat', { id: resumeId })));
|
|
781
800
|
await browser.openChat(resumeId);
|
|
782
801
|
freshChat = false;
|
|
783
802
|
sendSystemPrompt = resendPrompt;
|
|
784
803
|
saveLastChat(resumeId, currentWorkdir);
|
|
785
804
|
}
|
|
786
805
|
catch (e) {
|
|
787
|
-
console.error(theme.error(
|
|
806
|
+
console.error(theme.error(t('msg.open_chat_error', { v: e.message })));
|
|
788
807
|
}
|
|
789
808
|
}
|
|
790
809
|
await autoReload();
|
|
@@ -802,8 +821,8 @@ async function main() {
|
|
|
802
821
|
transcript.close();
|
|
803
822
|
return;
|
|
804
823
|
}
|
|
805
|
-
console.log(theme.system('
|
|
806
|
-
console.log(theme.system('
|
|
824
|
+
console.log(theme.system(t('msg.interactive')));
|
|
825
|
+
console.log(theme.system(t('msg.queue_hint')));
|
|
807
826
|
let freshChatNext = true;
|
|
808
827
|
let sendSystemPromptNext = true;
|
|
809
828
|
let lastChats = [];
|
|
@@ -824,7 +843,7 @@ async function main() {
|
|
|
824
843
|
// висеть отдельным процессом после выхода агента.
|
|
825
844
|
await mod.closeWeb().catch(() => { });
|
|
826
845
|
transcript.close();
|
|
827
|
-
console.log(theme.system('
|
|
846
|
+
console.log(theme.system(String.fromCharCode(10) + t('msg.bye')));
|
|
828
847
|
process.exit(0);
|
|
829
848
|
});
|
|
830
849
|
// По умолчанию при старте начинаем НОВЫЙ чат: контекст прошлой сессии
|
|
@@ -842,16 +861,16 @@ async function main() {
|
|
|
842
861
|
}
|
|
843
862
|
if (resumeId) {
|
|
844
863
|
try {
|
|
845
|
-
console.log(theme.system(
|
|
864
|
+
console.log(theme.system(t('msg.opening_chat', { id: resumeId })));
|
|
846
865
|
await browser.openChat(resumeId);
|
|
847
866
|
currentChatId = resumeId;
|
|
848
867
|
freshChatNext = false;
|
|
849
868
|
sendSystemPromptNext = resendPrompt;
|
|
850
869
|
saveLastChat(resumeId, currentWorkdir);
|
|
851
|
-
console.log(theme.system(
|
|
870
|
+
console.log(theme.system(t('msg.chat_opened') + ' ' + resumeId + String.fromCharCode(10)));
|
|
852
871
|
}
|
|
853
872
|
catch (e) {
|
|
854
|
-
console.error(theme.error(
|
|
873
|
+
console.error(theme.error(t('msg.open_chat_error', { v: e.message })));
|
|
855
874
|
}
|
|
856
875
|
}
|
|
857
876
|
// ---------- ввод: постоянная строка внизу + статус сверху ----------
|
|
@@ -872,7 +891,7 @@ async function main() {
|
|
|
872
891
|
let tail;
|
|
873
892
|
if (reviewMode) {
|
|
874
893
|
tail =
|
|
875
|
-
theme.warn('
|
|
894
|
+
theme.warn(t('prompt.review')) + theme.dim(':') + theme.dir(reviewMode.snapName);
|
|
876
895
|
}
|
|
877
896
|
else {
|
|
878
897
|
tail = theme.dir(dirLabel(currentWorkdir));
|
|
@@ -882,7 +901,7 @@ async function main() {
|
|
|
882
901
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
883
902
|
const ed = new LineEditor({
|
|
884
903
|
prompt: buildPrompt(),
|
|
885
|
-
commands:
|
|
904
|
+
commands: buildSlashCommands(),
|
|
886
905
|
});
|
|
887
906
|
editor = ed;
|
|
888
907
|
ed.onSubmit = (text) => {
|
|
@@ -895,13 +914,13 @@ async function main() {
|
|
|
895
914
|
};
|
|
896
915
|
ed.onEscape = () => {
|
|
897
916
|
if (ed.busy) {
|
|
898
|
-
ed.printAbove(theme.warn('
|
|
917
|
+
ed.printAbove(theme.warn(t('msg.abort_gen_short')));
|
|
899
918
|
browser.stopGeneration().catch(() => { });
|
|
900
919
|
}
|
|
901
920
|
};
|
|
902
921
|
ed.onCtrlC = () => {
|
|
903
922
|
if (ed.busy) {
|
|
904
|
-
ed.printAbove(theme.warn('
|
|
923
|
+
ed.printAbove(theme.warn(t('msg.abort_ctrlc_short')));
|
|
905
924
|
browser.stopGeneration().catch(() => { });
|
|
906
925
|
}
|
|
907
926
|
else {
|
|
@@ -937,6 +956,206 @@ async function main() {
|
|
|
937
956
|
edAny._origLog = origLog;
|
|
938
957
|
edAny._origErr = origErr;
|
|
939
958
|
}
|
|
959
|
+
// ---------- /config ----------
|
|
960
|
+
// Просмотр и правка настроек без перезапуска. Значения валидируются по
|
|
961
|
+
// CONFIG_SCHEMA (src/config.ts) и пишутся в проектный .zamesrc.json.
|
|
962
|
+
// Язык (ui.locale) применяется сразу: меняем currentLocale, пересобираем
|
|
963
|
+
// подсказки редактора и обновляем config.ui.locale (его читает agent-loop
|
|
964
|
+
// при следующей задаче).
|
|
965
|
+
//
|
|
966
|
+
// Без аргументов в интерактивном терминале открывается меню (стрелки,
|
|
967
|
+
// Enter — изменить, d — сбросить, q — выйти). Есть и текстовые подкоманды
|
|
968
|
+
// (list/get/set/reset/lang/path) — для скриптов и не-TTY.
|
|
969
|
+
function setConfigRuntime(path, value) {
|
|
970
|
+
const segs = path.split('.');
|
|
971
|
+
let obj = config;
|
|
972
|
+
for (let i = 0; i < segs.length - 1; i++) {
|
|
973
|
+
obj = obj[segs[i]];
|
|
974
|
+
}
|
|
975
|
+
obj[segs[segs.length - 1]] = value;
|
|
976
|
+
if (path === 'ui.locale' && isLocale(value)) {
|
|
977
|
+
currentLocale = value;
|
|
978
|
+
config.ui.locale = value;
|
|
979
|
+
if (editor) {
|
|
980
|
+
editor.setCommands(buildSlashCommands());
|
|
981
|
+
editor.setPrompt(buildPrompt());
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function configSetRaw(field, raw) {
|
|
986
|
+
const value = validateConfigValue(field, raw);
|
|
987
|
+
writeConfigValue('project', field.path, raw);
|
|
988
|
+
setConfigRuntime(field.path, value);
|
|
989
|
+
}
|
|
990
|
+
function configResetField(field) {
|
|
991
|
+
resetConfigValue('project', field.path);
|
|
992
|
+
// Возвращаем рантайм-значение к дефолту.
|
|
993
|
+
const def = getByPath(DEFAULTS, field.path);
|
|
994
|
+
setConfigRuntime(field.path, def);
|
|
995
|
+
}
|
|
996
|
+
function configLabel(field) {
|
|
997
|
+
return t(field.labelKey);
|
|
998
|
+
}
|
|
999
|
+
function configShowList() {
|
|
1000
|
+
console.log(theme.system(t('cfg.title')));
|
|
1001
|
+
let lastGroup = '';
|
|
1002
|
+
for (const f of CONFIG_SCHEMA) {
|
|
1003
|
+
const g = t(f.groupKey);
|
|
1004
|
+
if (g !== lastGroup) {
|
|
1005
|
+
console.log(theme.system('\n ' + g));
|
|
1006
|
+
lastGroup = g;
|
|
1007
|
+
}
|
|
1008
|
+
const cur = getByPath(config, f.path);
|
|
1009
|
+
const shown = cur === undefined
|
|
1010
|
+
? t('cfg.menu.default')
|
|
1011
|
+
: typeof cur === 'boolean'
|
|
1012
|
+
? cur
|
|
1013
|
+
? t('common.on')
|
|
1014
|
+
: t('common.off')
|
|
1015
|
+
: JSON.stringify(cur);
|
|
1016
|
+
const extra = f.values ? ' [' + f.values.join('|') + ']' : '';
|
|
1017
|
+
console.log(' ' +
|
|
1018
|
+
theme.user(f.path) +
|
|
1019
|
+
theme.dim(' = ') +
|
|
1020
|
+
theme.assistant(shown) +
|
|
1021
|
+
theme.dim(extra) +
|
|
1022
|
+
theme.dim(' # ' + configLabel(f)));
|
|
1023
|
+
}
|
|
1024
|
+
console.log(theme.dim('\n' + t('cfg.usage')));
|
|
1025
|
+
}
|
|
1026
|
+
async function configOpenMenu() {
|
|
1027
|
+
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
1028
|
+
configShowList();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
// Меню рисует напрямую в stdout и само читает клавиши. Чтобы его вывод
|
|
1032
|
+
// не накладывался на постоянную строку ввода LineEditor, на время меню
|
|
1033
|
+
// «ставим редактор на паузу», а после — возвращаем.
|
|
1034
|
+
if (editor)
|
|
1035
|
+
editor.pause();
|
|
1036
|
+
try {
|
|
1037
|
+
await runConfigMenu({
|
|
1038
|
+
fields: CONFIG_SCHEMA,
|
|
1039
|
+
t,
|
|
1040
|
+
get: (path) => getByPath(config, path),
|
|
1041
|
+
set: (field, raw) => configSetRaw(field, raw),
|
|
1042
|
+
reset: (field) => configResetField(field),
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
catch (e) {
|
|
1046
|
+
console.error(theme.error(String(e.message || e)));
|
|
1047
|
+
}
|
|
1048
|
+
finally {
|
|
1049
|
+
if (editor)
|
|
1050
|
+
editor.resume();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
async function handleConfigCommand(input) {
|
|
1054
|
+
const parts = input.trim().split(/\s+/);
|
|
1055
|
+
const sub = (parts[1] || '').toLowerCase();
|
|
1056
|
+
// Без подкоманды — меню (или текстовый список в не-TTY).
|
|
1057
|
+
if (!sub || sub === 'menu' || sub === 'ui') {
|
|
1058
|
+
await configOpenMenu();
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
if (sub === 'list' || sub === 'show' || sub === 'ls') {
|
|
1062
|
+
configShowList();
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (sub === 'path' || sub === 'paths') {
|
|
1066
|
+
console.log(theme.system(t('cfg.paths', {
|
|
1067
|
+
global: CONFIG_PATHS.HOME_CONFIG,
|
|
1068
|
+
project: CONFIG_PATHS.PROJECT_CONFIG,
|
|
1069
|
+
})));
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
// /config lang <ru|en> — быстрый доступ к ui.locale
|
|
1073
|
+
if (sub === 'lang' || sub === 'language' || sub === 'язык') {
|
|
1074
|
+
const val = parts[2];
|
|
1075
|
+
if (!val) {
|
|
1076
|
+
console.log(theme.system(t('status.locale', { v: localeDisplayName(currentLocale) })));
|
|
1077
|
+
console.log(theme.dim(t('cfg.lang_usage')));
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const field = CONFIG_SCHEMA.find((f) => f.path === 'ui.locale');
|
|
1081
|
+
const loc = normalizeLocale(val);
|
|
1082
|
+
try {
|
|
1083
|
+
configSetRaw(field, loc);
|
|
1084
|
+
}
|
|
1085
|
+
catch (e) {
|
|
1086
|
+
console.error(theme.error(t('cfg.write_error', { v: e.message })));
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
console.log(theme.assistant(t('cfg.lang_set', { v: localeDisplayName(loc) })));
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (sub === 'get') {
|
|
1093
|
+
const key = parts[2];
|
|
1094
|
+
if (!key) {
|
|
1095
|
+
console.log(theme.dim(t('cfg.usage')));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const field = CONFIG_SCHEMA.find((f) => f.path === key);
|
|
1099
|
+
if (!field) {
|
|
1100
|
+
console.error(theme.error(t('cfg.unknown_key', { v: key })));
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const cur = getByPath(config, key);
|
|
1104
|
+
console.log(theme.system(t('cfg.value', {
|
|
1105
|
+
v: key,
|
|
1106
|
+
value: cur === undefined ? t('cfg.menu.default') : JSON.stringify(cur),
|
|
1107
|
+
})));
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
if (sub === 'set') {
|
|
1111
|
+
const key = parts[2];
|
|
1112
|
+
const raw = parts.slice(3).join(' ');
|
|
1113
|
+
const field = CONFIG_SCHEMA.find((f) => f.path === key);
|
|
1114
|
+
if (!field) {
|
|
1115
|
+
console.error(theme.error(t('cfg.unknown_key', { v: key || '' })));
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (!raw) {
|
|
1119
|
+
console.log(theme.dim(t('cfg.usage')));
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
try {
|
|
1123
|
+
configSetRaw(field, raw);
|
|
1124
|
+
}
|
|
1125
|
+
catch {
|
|
1126
|
+
console.error(theme.error(t('cfg.bad_value', {
|
|
1127
|
+
v: key,
|
|
1128
|
+
type: field.values ? field.values.join('|') : field.type,
|
|
1129
|
+
})));
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
console.log(theme.assistant(t('cfg.saved', {
|
|
1133
|
+
v: key,
|
|
1134
|
+
value: JSON.stringify(getByPath(config, key)),
|
|
1135
|
+
})));
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
if (sub === 'reset' || sub === 'unset') {
|
|
1139
|
+
const key = parts[2];
|
|
1140
|
+
const field = CONFIG_SCHEMA.find((f) => f.path === key);
|
|
1141
|
+
if (!field) {
|
|
1142
|
+
console.error(theme.error(t('cfg.unknown_key', { v: key || '' })));
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
try {
|
|
1146
|
+
configResetField(field);
|
|
1147
|
+
}
|
|
1148
|
+
catch (e) {
|
|
1149
|
+
console.error(theme.error(t('cfg.write_error', { v: e.message })));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
console.log(theme.assistant(t('cfg.reset', { v: key })));
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
// Неизвестная подкоманда — показываем список.
|
|
1156
|
+
console.error(theme.error(t('cfg.unknown_key', { v: sub })));
|
|
1157
|
+
configShowList();
|
|
1158
|
+
}
|
|
940
1159
|
while (running) {
|
|
941
1160
|
let input;
|
|
942
1161
|
try {
|
|
@@ -948,7 +1167,7 @@ async function main() {
|
|
|
948
1167
|
let tail;
|
|
949
1168
|
if (reviewMode) {
|
|
950
1169
|
tail =
|
|
951
|
-
theme.warn('
|
|
1170
|
+
theme.warn(t('prompt.review')) +
|
|
952
1171
|
theme.dim(':') +
|
|
953
1172
|
theme.dir(reviewMode.snapName);
|
|
954
1173
|
}
|
|
@@ -972,7 +1191,7 @@ async function main() {
|
|
|
972
1191
|
continue;
|
|
973
1192
|
}
|
|
974
1193
|
if (['/new', '/clear', 'new'].includes(lower)) {
|
|
975
|
-
console.log(theme.system('
|
|
1194
|
+
console.log(theme.system(t('msg.new_chat')));
|
|
976
1195
|
try {
|
|
977
1196
|
await browser.newChat();
|
|
978
1197
|
freshChatNext = false;
|
|
@@ -980,10 +1199,10 @@ async function main() {
|
|
|
980
1199
|
currentChatId = await browser.getCurrentChatId();
|
|
981
1200
|
saveLastChat(currentChatId, currentWorkdir);
|
|
982
1201
|
transcript.log('new_chat');
|
|
983
|
-
console.log(theme.system('
|
|
1202
|
+
console.log(theme.system(t('msg.new_chat_ok') + String.fromCharCode(10)));
|
|
984
1203
|
}
|
|
985
1204
|
catch (e) {
|
|
986
|
-
console.error(theme.error('
|
|
1205
|
+
console.error(theme.error(t('msg.new_chat_error', { v: e.message })), e.message);
|
|
987
1206
|
}
|
|
988
1207
|
continue;
|
|
989
1208
|
}
|
|
@@ -1015,16 +1234,10 @@ async function main() {
|
|
|
1015
1234
|
sendSystemPromptNext = false;
|
|
1016
1235
|
currentChatId = await browser.getCurrentChatId();
|
|
1017
1236
|
saveLastChat(currentChatId, currentWorkdir);
|
|
1018
|
-
console.log(theme.user('
|
|
1019
|
-
' «исправь ошибки»\n' +
|
|
1020
|
-
' «доработай обработку ошибок в ask()»\n' +
|
|
1021
|
-
' «покажи, что не так с undo»\n' +
|
|
1022
|
-
'Выйти: /self-done. Применить: /self-apply ' +
|
|
1023
|
-
reviewMode.snapName +
|
|
1024
|
-
'\n'));
|
|
1237
|
+
console.log(theme.user(t('self.review_hint', { name: reviewMode.snapName })));
|
|
1025
1238
|
}
|
|
1026
1239
|
catch (e) {
|
|
1027
|
-
console.error(theme.error('
|
|
1240
|
+
console.error(theme.error(t('self.review_failed')), e.message);
|
|
1028
1241
|
if (debug)
|
|
1029
1242
|
console.error(e.stack);
|
|
1030
1243
|
}
|
|
@@ -1033,7 +1246,7 @@ async function main() {
|
|
|
1033
1246
|
if (lower === '/self-fix' || lower.startsWith('/self-fix ')) {
|
|
1034
1247
|
const rest = trimmed.slice('/self-fix'.length).trim();
|
|
1035
1248
|
if (!rest) {
|
|
1036
|
-
console.error(theme.error('
|
|
1249
|
+
console.error(theme.error(t('self.fix_usage')));
|
|
1037
1250
|
continue;
|
|
1038
1251
|
}
|
|
1039
1252
|
const sp = rest.indexOf(' ');
|
|
@@ -1042,7 +1255,7 @@ async function main() {
|
|
|
1042
1255
|
const snapRoot = path.join(ZAMES_HOME, 'snapshots', name);
|
|
1043
1256
|
const stat = await fs.stat(snapRoot).catch(() => null);
|
|
1044
1257
|
if (!stat || !stat.isDirectory()) {
|
|
1045
|
-
console.error(theme.error(
|
|
1258
|
+
console.error(theme.error(t('self.snapshot_not_found', { v: snapRoot })));
|
|
1046
1259
|
continue;
|
|
1047
1260
|
}
|
|
1048
1261
|
const originalWorkdir = reviewMode
|
|
@@ -1051,17 +1264,17 @@ async function main() {
|
|
|
1051
1264
|
// Свежий чат + review-промпт на этот снапшот
|
|
1052
1265
|
try {
|
|
1053
1266
|
const { runAgentLoop: ral } = await import('./agent-loop.js');
|
|
1054
|
-
const { buildSystemPrompt } = await import('./system-prompt.js');
|
|
1055
1267
|
const tools = mod.createTools(snapRoot, { undo: null });
|
|
1056
1268
|
await browser.newChat();
|
|
1057
1269
|
const sysPrompt = mod.buildSystemPrompt({
|
|
1058
1270
|
workdir: snapRoot,
|
|
1059
1271
|
tools,
|
|
1272
|
+
locale: currentLocale,
|
|
1060
1273
|
});
|
|
1061
|
-
console.log(theme.system('
|
|
1274
|
+
console.log(theme.system(t('self.init_review_chat')));
|
|
1062
1275
|
await browser.ask(sysPrompt, { timeout: 60_000 });
|
|
1063
1276
|
if (focus) {
|
|
1064
|
-
const ui = mod.createSpinner();
|
|
1277
|
+
const ui = mod.createSpinner(currentLocale);
|
|
1065
1278
|
ui.thinking();
|
|
1066
1279
|
await ral({
|
|
1067
1280
|
browser,
|
|
@@ -1090,16 +1303,16 @@ async function main() {
|
|
|
1090
1303
|
sendSystemPromptNext = false;
|
|
1091
1304
|
currentChatId = await browser.getCurrentChatId();
|
|
1092
1305
|
saveLastChat(currentChatId, currentWorkdir);
|
|
1093
|
-
console.log(theme.user(
|
|
1306
|
+
console.log(theme.user(t('self.fix_hint', { name })));
|
|
1094
1307
|
}
|
|
1095
1308
|
catch (e) {
|
|
1096
|
-
console.error(theme.error('
|
|
1309
|
+
console.error(theme.error(t('self.enter_failed')), e.message);
|
|
1097
1310
|
}
|
|
1098
1311
|
continue;
|
|
1099
1312
|
}
|
|
1100
1313
|
if (lower === '/self-done') {
|
|
1101
1314
|
if (!reviewMode) {
|
|
1102
|
-
console.log(theme.system('
|
|
1315
|
+
console.log(theme.system(t('self.not_in_review')));
|
|
1103
1316
|
continue;
|
|
1104
1317
|
}
|
|
1105
1318
|
const back = reviewMode.originalWorkdir;
|
|
@@ -1108,7 +1321,7 @@ async function main() {
|
|
|
1108
1321
|
// Раз чат занят review-контекстом, для обычной работы создадим новый
|
|
1109
1322
|
freshChatNext = true;
|
|
1110
1323
|
sendSystemPromptNext = true;
|
|
1111
|
-
console.log(theme.system(
|
|
1324
|
+
console.log(theme.system(t('self.done_hint', { v: back })));
|
|
1112
1325
|
continue;
|
|
1113
1326
|
}
|
|
1114
1327
|
if (lower === '/self-list') {
|
|
@@ -1123,7 +1336,7 @@ async function main() {
|
|
|
1123
1336
|
if (lower === '/self-diff' || lower.startsWith('/self-diff ')) {
|
|
1124
1337
|
const name = trimmed.slice('/self-diff'.length).trim();
|
|
1125
1338
|
if (!name) {
|
|
1126
|
-
console.error(theme.error('
|
|
1339
|
+
console.error(theme.error(t('self.diff_usage')));
|
|
1127
1340
|
continue;
|
|
1128
1341
|
}
|
|
1129
1342
|
try {
|
|
@@ -1137,7 +1350,7 @@ async function main() {
|
|
|
1137
1350
|
if (lower === '/self-apply' || lower.startsWith('/self-apply ')) {
|
|
1138
1351
|
const name = trimmed.slice('/self-apply'.length).trim();
|
|
1139
1352
|
if (!name) {
|
|
1140
|
-
console.error(theme.error('
|
|
1353
|
+
console.error(theme.error(t('self.apply_usage')));
|
|
1141
1354
|
continue;
|
|
1142
1355
|
}
|
|
1143
1356
|
try {
|
|
@@ -1150,7 +1363,7 @@ async function main() {
|
|
|
1150
1363
|
}
|
|
1151
1364
|
// ---------- Обычные команды ----------
|
|
1152
1365
|
if (lower === '/chats') {
|
|
1153
|
-
const spin = editor || mod.createSpinner();
|
|
1366
|
+
const spin = editor || mod.createSpinner(currentLocale);
|
|
1154
1367
|
spin.thinking();
|
|
1155
1368
|
try {
|
|
1156
1369
|
lastChats = await browser.listChats(30);
|
|
@@ -1159,41 +1372,41 @@ async function main() {
|
|
|
1159
1372
|
console.log(theme.system('Чатов не найдено. Возможно, сайдбар свёрнут или селекторы устарели.'));
|
|
1160
1373
|
}
|
|
1161
1374
|
else {
|
|
1162
|
-
console.log(theme.system('
|
|
1375
|
+
console.log(theme.system(t('chats.recent')));
|
|
1163
1376
|
lastChats.forEach((c, i) => {
|
|
1164
1377
|
const n = String(i + 1).padStart(2, ' ');
|
|
1165
1378
|
console.log(` ${theme.user(n)}. ${c.title} ${theme.system('(' + c.id.slice(0, 8) + '…)')}`);
|
|
1166
1379
|
});
|
|
1167
|
-
console.log(theme.system('
|
|
1380
|
+
console.log(theme.system(t('chats.use_resume')));
|
|
1168
1381
|
}
|
|
1169
1382
|
}
|
|
1170
1383
|
catch (e) {
|
|
1171
1384
|
spin.stop();
|
|
1172
|
-
console.error(theme.error('
|
|
1385
|
+
console.error(theme.error(t('chats.fetch_error')), e.message);
|
|
1173
1386
|
}
|
|
1174
1387
|
continue;
|
|
1175
1388
|
}
|
|
1176
1389
|
if (lower === '/resume' || lower.startsWith('/resume ')) {
|
|
1177
1390
|
const arg = trimmed.slice(7).trim();
|
|
1178
1391
|
if (!arg) {
|
|
1179
|
-
console.error(theme.error('
|
|
1392
|
+
console.error(theme.error(t('chats.resume_usage')));
|
|
1180
1393
|
continue;
|
|
1181
1394
|
}
|
|
1182
1395
|
const n = Number(arg);
|
|
1183
1396
|
if (!Number.isFinite(n) || n < 1) {
|
|
1184
|
-
console.error(theme.error('
|
|
1397
|
+
console.error(theme.error(t('chats.need_number')));
|
|
1185
1398
|
continue;
|
|
1186
1399
|
}
|
|
1187
1400
|
if (!lastChats.length) {
|
|
1188
|
-
console.error(theme.error('
|
|
1401
|
+
console.error(theme.error(t('chats.run_chats_first')));
|
|
1189
1402
|
continue;
|
|
1190
1403
|
}
|
|
1191
1404
|
const pick = lastChats[n - 1];
|
|
1192
1405
|
if (!pick) {
|
|
1193
|
-
console.error(theme.error(
|
|
1406
|
+
console.error(theme.error(t('chats.no_n', { n, total: lastChats.length })));
|
|
1194
1407
|
continue;
|
|
1195
1408
|
}
|
|
1196
|
-
console.log(theme.system(
|
|
1409
|
+
console.log(theme.system(t('chats.opening', { v: pick.title })));
|
|
1197
1410
|
try {
|
|
1198
1411
|
await browser.openChat(pick.id);
|
|
1199
1412
|
currentChatId = pick.id;
|
|
@@ -1201,32 +1414,32 @@ async function main() {
|
|
|
1201
1414
|
sendSystemPromptNext = resendPrompt;
|
|
1202
1415
|
saveLastChat(pick.id, currentWorkdir, pick.title);
|
|
1203
1416
|
transcript.log('resume_chat', { id: pick.id, title: pick.title });
|
|
1204
|
-
console.log(theme.assistant(
|
|
1417
|
+
console.log(theme.assistant(t('msg.chat_opened')) +
|
|
1205
1418
|
theme.system(resendPrompt
|
|
1206
1419
|
? ' Системный промпт будет переслан на следующей задаче.\n'
|
|
1207
1420
|
: ' Контекст чата сохранён. Системный промпт не пересылается (--resend-prompt чтобы дослать).\n'));
|
|
1208
1421
|
}
|
|
1209
1422
|
catch (e) {
|
|
1210
|
-
console.error(theme.error('
|
|
1423
|
+
console.error(theme.error(t('msg.open_chat_error', { v: '' })), e.message);
|
|
1211
1424
|
}
|
|
1212
1425
|
continue;
|
|
1213
1426
|
}
|
|
1214
1427
|
if (lower === '/chat') {
|
|
1215
1428
|
if (currentChatId) {
|
|
1216
|
-
console.log(theme.system(
|
|
1429
|
+
console.log(theme.system(t('chats.current_id', { v: currentChatId })));
|
|
1217
1430
|
console.log(theme.system(`URL: https://chat.deepseek.com/a/chat/s/${currentChatId}`));
|
|
1218
1431
|
}
|
|
1219
1432
|
else {
|
|
1220
1433
|
const id = await browser.getCurrentChatId();
|
|
1221
|
-
console.log(theme.system(id ?
|
|
1434
|
+
console.log(theme.system(id ? t('chats.current_id', { v: id }) : t('chats.not_created')));
|
|
1222
1435
|
}
|
|
1223
1436
|
continue;
|
|
1224
1437
|
}
|
|
1225
1438
|
if (lower === '/sessions' || lower === '/session') {
|
|
1226
1439
|
const all = listSessions();
|
|
1227
|
-
console.log(theme.system(
|
|
1440
|
+
console.log(theme.system(t('sessions.dir', { v: sessionsDir() })));
|
|
1228
1441
|
if (!all.length) {
|
|
1229
|
-
console.log(theme.system('
|
|
1442
|
+
console.log(theme.system(t('sessions.none')));
|
|
1230
1443
|
}
|
|
1231
1444
|
else {
|
|
1232
1445
|
all.forEach((s, i) => {
|
|
@@ -1236,18 +1449,18 @@ async function main() {
|
|
|
1236
1449
|
const wd = s.workdir ? theme.dim(` [${dirLabel(s.workdir)}]`) : '';
|
|
1237
1450
|
console.log(` ${theme.user(n)}. ${s.id.slice(0, 8)}…${title}${wd}${mark}`);
|
|
1238
1451
|
});
|
|
1239
|
-
console.log(theme.system('
|
|
1452
|
+
console.log(theme.system(t('sessions.restore_hint')));
|
|
1240
1453
|
}
|
|
1241
1454
|
continue;
|
|
1242
1455
|
}
|
|
1243
1456
|
if (lower.startsWith('/resume-id ')) {
|
|
1244
1457
|
const id = trimmed.slice('/resume-id'.length).trim();
|
|
1245
1458
|
if (!id) {
|
|
1246
|
-
console.error(theme.error('
|
|
1459
|
+
console.error(theme.error(t('sessions.resume_id_usage')));
|
|
1247
1460
|
continue;
|
|
1248
1461
|
}
|
|
1249
1462
|
try {
|
|
1250
|
-
console.log(theme.system(
|
|
1463
|
+
console.log(theme.system(t('msg.opening_chat', { id })));
|
|
1251
1464
|
await browser.openChat(id);
|
|
1252
1465
|
currentChatId = id;
|
|
1253
1466
|
freshChatNext = false;
|
|
@@ -1258,7 +1471,7 @@ async function main() {
|
|
|
1258
1471
|
theme.system(String.fromCharCode(10)));
|
|
1259
1472
|
}
|
|
1260
1473
|
catch (e) {
|
|
1261
|
-
console.error(theme.error('
|
|
1474
|
+
console.error(theme.error(t('msg.open_chat_error', { v: '' })), e.message);
|
|
1262
1475
|
}
|
|
1263
1476
|
continue;
|
|
1264
1477
|
}
|
|
@@ -1267,11 +1480,11 @@ async function main() {
|
|
|
1267
1480
|
continue;
|
|
1268
1481
|
}
|
|
1269
1482
|
if (lower === '/reload') {
|
|
1270
|
-
console.log(theme.system('
|
|
1483
|
+
console.log(theme.system(t('msg.reload_start')));
|
|
1271
1484
|
try {
|
|
1272
1485
|
const { count, errors } = await reloadModules();
|
|
1273
1486
|
if (errors.length) {
|
|
1274
|
-
console.error(theme.error('
|
|
1487
|
+
console.error(theme.error(t('msg.reload_partial')));
|
|
1275
1488
|
for (const e of errors)
|
|
1276
1489
|
console.error(theme.error(' ' + e));
|
|
1277
1490
|
}
|
|
@@ -1280,59 +1493,72 @@ async function main() {
|
|
|
1280
1493
|
}
|
|
1281
1494
|
}
|
|
1282
1495
|
catch (e) {
|
|
1283
|
-
console.error(theme.error('
|
|
1496
|
+
console.error(theme.error(t('msg.reload_error')), e.message);
|
|
1284
1497
|
}
|
|
1285
1498
|
continue;
|
|
1286
1499
|
}
|
|
1287
1500
|
if (lower === '/status') {
|
|
1288
|
-
|
|
1289
|
-
|
|
1501
|
+
const yes = t('common.yes');
|
|
1502
|
+
const no = t('common.no');
|
|
1503
|
+
console.log(theme.system(t('status.workdir', { v: currentWorkdir })));
|
|
1504
|
+
console.log(theme.system(t('status.review_mode', {
|
|
1505
|
+
v: reviewMode ? reviewMode.snapName : t('status.review_none'),
|
|
1506
|
+
})));
|
|
1290
1507
|
if (reviewMode) {
|
|
1291
|
-
console.log(theme.system(
|
|
1292
|
-
}
|
|
1293
|
-
console.log(theme.system(
|
|
1294
|
-
console.log(theme.system(
|
|
1295
|
-
console.log(theme.system(
|
|
1296
|
-
console.log(theme.system(
|
|
1297
|
-
console.log(theme.system(
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
console.log(theme.system(
|
|
1301
|
-
console.log(theme.system(
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
console.log(theme.system(
|
|
1508
|
+
console.log(theme.system(t('status.orig_dir', { v: reviewMode.originalWorkdir })));
|
|
1509
|
+
}
|
|
1510
|
+
console.log(theme.system(t('status.chat', { v: currentChatId || t('common.none') })));
|
|
1511
|
+
console.log(theme.system(t('status.fresh_next', { v: freshChatNext ? yes : no })));
|
|
1512
|
+
console.log(theme.system(t('status.prompt_next', { v: sendSystemPromptNext ? yes : no })));
|
|
1513
|
+
console.log(theme.system(t('status.resend', { v: resendPrompt ? yes : no })));
|
|
1514
|
+
console.log(theme.system(t('status.last_chat', {
|
|
1515
|
+
v: loadLastChat(currentWorkdir) || t('common.none'),
|
|
1516
|
+
})));
|
|
1517
|
+
console.log(theme.system(t('status.sessions', { v: sessionsDir() })));
|
|
1518
|
+
console.log(theme.system(t('status.dev', {
|
|
1519
|
+
v: devMode ? t('common.on') : t('common.off'),
|
|
1520
|
+
})));
|
|
1521
|
+
console.log(theme.system(t('status.max_iter', { v: maxIter })));
|
|
1522
|
+
console.log(theme.system(t('status.headless', { v: headless ? yes : no })));
|
|
1523
|
+
console.log(theme.system(t('status.debug', { v: debug ? yes : no })));
|
|
1524
|
+
console.log(theme.system(t('status.undo', {
|
|
1525
|
+
v: config.undo.enabled ? t('common.on') : t('common.off'),
|
|
1526
|
+
})));
|
|
1527
|
+
console.log(theme.system(t('status.transcript', {
|
|
1528
|
+
v: transcript.file || t('common.off'),
|
|
1529
|
+
})));
|
|
1530
|
+
console.log(theme.system(t('status.locale', { v: localeDisplayName(currentLocale) })));
|
|
1305
1531
|
continue;
|
|
1306
1532
|
}
|
|
1307
|
-
if (lower === '/config') {
|
|
1308
|
-
|
|
1533
|
+
if (lower === '/config' || lower.startsWith('/config ')) {
|
|
1534
|
+
await handleConfigCommand(trimmed);
|
|
1309
1535
|
continue;
|
|
1310
1536
|
}
|
|
1311
1537
|
if (lower === '/transcript') {
|
|
1312
|
-
console.log(theme.system(transcript.file ||
|
|
1538
|
+
console.log(theme.system(transcript.file || t('common.off')));
|
|
1313
1539
|
continue;
|
|
1314
1540
|
}
|
|
1315
1541
|
if (lower === '/undo') {
|
|
1316
1542
|
const result = await undo.undoLast();
|
|
1317
1543
|
if (result.ok && result.record) {
|
|
1318
|
-
console.log(theme.assistant(
|
|
1319
|
-
theme.system(result.record.existed ? '
|
|
1544
|
+
console.log(theme.assistant(t('undo.reverted', { v: result.record.originalPath })) +
|
|
1545
|
+
theme.system(result.record.existed ? t('undo.restored') : t('undo.deleted')));
|
|
1320
1546
|
transcript.log('undo', { path: result.record.originalPath });
|
|
1321
1547
|
}
|
|
1322
1548
|
else {
|
|
1323
|
-
console.error(theme.error(
|
|
1549
|
+
console.error(theme.error(t('undo.failed', { v: String(result.reason ?? '') })));
|
|
1324
1550
|
}
|
|
1325
1551
|
continue;
|
|
1326
1552
|
}
|
|
1327
1553
|
if (lower === '/undo-list' || lower === '/history') {
|
|
1328
1554
|
const list = await undo.list(10);
|
|
1329
1555
|
if (!list.length) {
|
|
1330
|
-
console.log(theme.system('
|
|
1556
|
+
console.log(theme.system(t('undo.empty')));
|
|
1331
1557
|
}
|
|
1332
1558
|
else {
|
|
1333
1559
|
for (const r of list) {
|
|
1334
1560
|
const stamp = new Date(r.stamp).toLocaleString();
|
|
1335
|
-
const flag = r.existed ? '
|
|
1561
|
+
const flag = r.existed ? t('undo.changed') : t('undo.created');
|
|
1336
1562
|
console.log(theme.system(`${stamp} [${flag}] ${r.originalPath}`));
|
|
1337
1563
|
}
|
|
1338
1564
|
}
|
|
@@ -1343,12 +1569,12 @@ async function main() {
|
|
|
1343
1569
|
const file = path.join(config.transcript.dir, `dom-${stamp}.html`);
|
|
1344
1570
|
try {
|
|
1345
1571
|
const result = await browser.dumpDom(file);
|
|
1346
|
-
console.log(theme.assistant(
|
|
1347
|
-
console.log(theme.system('
|
|
1572
|
+
console.log(theme.assistant(t('dom.saved', { v: result.file })));
|
|
1573
|
+
console.log(theme.system(t('dom.selectors')));
|
|
1348
1574
|
console.log(JSON.stringify(result.selectors, null, 2));
|
|
1349
1575
|
}
|
|
1350
1576
|
catch (e) {
|
|
1351
|
-
console.error(theme.error('
|
|
1577
|
+
console.error(theme.error(t('dom.save_error')), e.message);
|
|
1352
1578
|
}
|
|
1353
1579
|
continue;
|
|
1354
1580
|
}
|
|
@@ -1360,34 +1586,34 @@ async function main() {
|
|
|
1360
1586
|
: sandboxRoot;
|
|
1361
1587
|
const rel = path.relative(sandboxRoot, newDir);
|
|
1362
1588
|
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
1363
|
-
console.error(theme.error('
|
|
1589
|
+
console.error(theme.error(t('cd.outside', { v: sandboxRoot })));
|
|
1364
1590
|
continue;
|
|
1365
1591
|
}
|
|
1366
1592
|
const stat = await fs.stat(newDir).catch(() => null);
|
|
1367
1593
|
if (!stat || !stat.isDirectory()) {
|
|
1368
|
-
console.error(theme.error('
|
|
1594
|
+
console.error(theme.error(t('cd.not_dir', { v: newDir })));
|
|
1369
1595
|
continue;
|
|
1370
1596
|
}
|
|
1371
1597
|
if (newDir === currentWorkdir) {
|
|
1372
|
-
console.log(theme.system('
|
|
1598
|
+
console.log(theme.system(t('cd.already')));
|
|
1373
1599
|
continue;
|
|
1374
1600
|
}
|
|
1375
1601
|
if (reviewMode) {
|
|
1376
|
-
console.log(theme.system('
|
|
1602
|
+
console.log(theme.system(t('cd.left_review')));
|
|
1377
1603
|
reviewMode = null;
|
|
1378
1604
|
}
|
|
1379
1605
|
currentWorkdir = newDir;
|
|
1380
1606
|
freshChatNext = true;
|
|
1381
1607
|
sendSystemPromptNext = true;
|
|
1382
|
-
console.log(theme.system('
|
|
1608
|
+
console.log(theme.system(t('cd.changed', { v: newDir })));
|
|
1383
1609
|
}
|
|
1384
1610
|
catch (e) {
|
|
1385
|
-
console.error(theme.error('
|
|
1611
|
+
console.error(theme.error(t('cd.failed', { v: e.message })));
|
|
1386
1612
|
}
|
|
1387
1613
|
continue;
|
|
1388
1614
|
}
|
|
1389
1615
|
if (lower.startsWith('/')) {
|
|
1390
|
-
console.error(theme.error(
|
|
1616
|
+
console.error(theme.error(t('msg.unknown_cmd', { v: trimmed })));
|
|
1391
1617
|
continue;
|
|
1392
1618
|
}
|
|
1393
1619
|
// ---- Обычная задача (в том числе в review-режиме) ----
|
|
@@ -1433,7 +1659,7 @@ async function main() {
|
|
|
1433
1659
|
transcript.close();
|
|
1434
1660
|
}
|
|
1435
1661
|
main().catch((e) => {
|
|
1436
|
-
console.error(theme.error('
|
|
1662
|
+
console.error(theme.error(t('msg.critical')), e.message);
|
|
1437
1663
|
if (debug)
|
|
1438
1664
|
console.error(e.stack);
|
|
1439
1665
|
process.exit(1);
|