winter-super-cli 2026.6.6 → 2026.6.7
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/bin/winter.js +1 -0
- package/package.json +1 -1
- package/src/agent/runtime.js +10 -16
- package/src/ai/model-capabilities.js +15 -0
- package/src/ai/prompts/system-prompt.js +26 -4
- package/src/ai/providers.js +132 -55
- package/src/ai/small-model-amplifier.js +2 -2
- package/src/cli/commands.js +12 -0
- package/src/cli/context-loader.js +1 -1
- package/src/cli/input-controller.js +55 -44
- package/src/cli/prompt-builder.js +20 -11
- package/src/cli/repl-commands.js +3 -0
- package/src/cli/repl.js +187 -322
- package/src/cli/slash-commands.js +1 -0
- package/src/cli/snowflake-logo.js +64 -86
- package/src/cli/terminal-ui.js +139 -85
- package/src/cli/tool-runtime.js +8 -3
- package/src/cli/tui.js +181 -0
- package/src/context/token-juice.js +37 -10
- package/src/tools/executor.js +78 -3
package/src/cli/repl.js
CHANGED
|
@@ -6,8 +6,16 @@
|
|
|
6
6
|
import readline from 'readline';
|
|
7
7
|
import { promises as fs, watch as fsWatch } from 'fs';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
|
-
import {
|
|
9
|
+
import { colors, applyColorTheme, welcomeBanner } from './snowflake-logo.js';
|
|
10
10
|
import { renderBox, supportsUnicodeUi, terminalWidth, stripAnsi, wrapText, padVisible } from './terminal-ui.js';
|
|
11
|
+
import {
|
|
12
|
+
buildTuiSnapshot,
|
|
13
|
+
renderAssistantPanel,
|
|
14
|
+
renderConversationStartup,
|
|
15
|
+
renderLandingTui,
|
|
16
|
+
renderStartupTui,
|
|
17
|
+
renderStatusPanel,
|
|
18
|
+
} from './tui.js';
|
|
11
19
|
import { WinterInputController } from './input-controller.js';
|
|
12
20
|
import { ToolExecutor } from '../tools/executor.js';
|
|
13
21
|
import { SessionManager } from '../session/manager.js';
|
|
@@ -30,7 +38,7 @@ import {
|
|
|
30
38
|
buildPromptToolResultWithTokenJuice,
|
|
31
39
|
} from './tool-runtime.js';
|
|
32
40
|
import { TokenJuice } from '../context/token-juice.js';
|
|
33
|
-
import { classifyModelTier } from '../ai/model-capabilities.js';
|
|
41
|
+
import { classifyModelTier, getModelBudgetMultiplier } from '../ai/model-capabilities.js';
|
|
34
42
|
import {
|
|
35
43
|
addUsage as mergeUsage,
|
|
36
44
|
buildToolCallSignature as buildToolCallSignatureText,
|
|
@@ -88,6 +96,7 @@ export class WinterREPL {
|
|
|
88
96
|
this.taskQueue = [];
|
|
89
97
|
this.isProcessing = false;
|
|
90
98
|
this.isCancelled = false;
|
|
99
|
+
this.currentAbortController = null;
|
|
91
100
|
this.sessionPermissionGrants = new Set();
|
|
92
101
|
this.permissionManager = new PermissionManager(this.config, this.session);
|
|
93
102
|
this.contextLoader = new ContextLoader({ projectPath: this.projectPath, session: this.session, tools: this.tools });
|
|
@@ -114,6 +123,8 @@ export class WinterREPL {
|
|
|
114
123
|
this.useUnicodeUi = supportsUnicodeUi();
|
|
115
124
|
this.inputController = new WinterInputController(this);
|
|
116
125
|
this.watchers = [];
|
|
126
|
+
this.startupNotices = [];
|
|
127
|
+
this._fixedPanel = Boolean(process.stdout.isTTY) && process.env.WINTER_FIXED_PANEL_TUI !== '0';
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
async initCodebaseSearch() {
|
|
@@ -147,6 +158,15 @@ export class WinterREPL {
|
|
|
147
158
|
});
|
|
148
159
|
}
|
|
149
160
|
|
|
161
|
+
startupNotice(message) {
|
|
162
|
+
const text = String(message || '').trim();
|
|
163
|
+
if (!text) return;
|
|
164
|
+
this.startupNotices.push(text);
|
|
165
|
+
if (this.startupNotices.length > 6) {
|
|
166
|
+
this.startupNotices = this.startupNotices.slice(-6);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
150
170
|
async startCodebaseWatcher() {
|
|
151
171
|
if (this.codebaseWatcher) return;
|
|
152
172
|
await this.initCodebaseSearch();
|
|
@@ -240,6 +260,7 @@ export class WinterREPL {
|
|
|
240
260
|
|
|
241
261
|
async buildCodebaseContext(task = '') {
|
|
242
262
|
try {
|
|
263
|
+
const modelTier = this.getActiveModelTier();
|
|
243
264
|
const stats = await this.ensureCodebaseIndex({ verbose: false });
|
|
244
265
|
if (!stats.totalChunks) return '';
|
|
245
266
|
|
|
@@ -278,7 +299,7 @@ export class WinterREPL {
|
|
|
278
299
|
}
|
|
279
300
|
}
|
|
280
301
|
|
|
281
|
-
return this.compactText(lines.join('\n'),
|
|
302
|
+
return this.compactText(lines.join('\n'), this.getCodebaseContextBudget(modelTier), 'codebase context');
|
|
282
303
|
} catch (error) {
|
|
283
304
|
return `[Codebase Index]\nUnavailable: ${error.message}`;
|
|
284
305
|
}
|
|
@@ -326,18 +347,22 @@ export class WinterREPL {
|
|
|
326
347
|
toolName,
|
|
327
348
|
result,
|
|
328
349
|
compact: this.shouldUseCompactPrompt(),
|
|
350
|
+
modelTier: this.getActiveModelTier(),
|
|
329
351
|
compactText: (text, maxChars, label) => this.compactText(text, maxChars, label),
|
|
330
352
|
summarizeToolResult: value => this.tools?.summarizeToolResult?.(value) || { ...value },
|
|
331
353
|
});
|
|
332
354
|
}
|
|
333
355
|
|
|
334
356
|
async buildPromptToolResultForModel(toolName, result) {
|
|
357
|
+
const modelTier = this.getActiveModelTier();
|
|
358
|
+
const tokenJuice = this.getTokenJuiceForModelTier(modelTier);
|
|
335
359
|
return buildPromptToolResultWithTokenJuice({
|
|
336
360
|
toolName,
|
|
337
361
|
result,
|
|
338
362
|
projectPath: this.projectPath,
|
|
339
|
-
tokenJuice
|
|
363
|
+
tokenJuice,
|
|
340
364
|
compact: this.shouldUseCompactPrompt(),
|
|
365
|
+
modelTier,
|
|
341
366
|
compactText: (text, maxChars, label) => this.compactText(text, maxChars, label),
|
|
342
367
|
summarizeToolResult: value => this.tools?.summarizeToolResult?.(value) || { ...value },
|
|
343
368
|
});
|
|
@@ -471,7 +496,7 @@ export class WinterREPL {
|
|
|
471
496
|
const fileName = path.basename(targetPath);
|
|
472
497
|
const memoryKey = `[Tự động ghi nhớ file ${fileName}]`;
|
|
473
498
|
await this.session.replaceMemory(memoryKey, content);
|
|
474
|
-
|
|
499
|
+
this.startupNotice(`loaded ${fileName}`);
|
|
475
500
|
continue;
|
|
476
501
|
}
|
|
477
502
|
|
|
@@ -487,7 +512,7 @@ export class WinterREPL {
|
|
|
487
512
|
const content = await fsPromises.readFile(p, 'utf8');
|
|
488
513
|
const memoryKey = `[Tự động ghi nhớ file ${path.basename(targetPath)}/${c}]`;
|
|
489
514
|
await this.session.replaceMemory(memoryKey, content);
|
|
490
|
-
|
|
515
|
+
this.startupNotice(`loaded ${path.basename(targetPath)}/${c}`);
|
|
491
516
|
loaded = true;
|
|
492
517
|
break;
|
|
493
518
|
}
|
|
@@ -512,7 +537,7 @@ export class WinterREPL {
|
|
|
512
537
|
for (const file of projectInstructionFiles) {
|
|
513
538
|
const memoryKey = `[Quy tắc dự án từ ${file.relativePath}]`;
|
|
514
539
|
await this.session.replaceMemory(memoryKey, file.content);
|
|
515
|
-
|
|
540
|
+
this.startupNotice(`rules ${file.relativePath}`);
|
|
516
541
|
}
|
|
517
542
|
}
|
|
518
543
|
} catch (e) {
|
|
@@ -551,8 +576,8 @@ export class WinterREPL {
|
|
|
551
576
|
try {
|
|
552
577
|
const projectWinterMd = path.join(this.projectPath, 'winter.md');
|
|
553
578
|
await fsPromises.writeFile(projectWinterMd, template, 'utf8');
|
|
554
|
-
|
|
555
|
-
|
|
579
|
+
this.startupNotice('created winter.md');
|
|
580
|
+
|
|
556
581
|
|
|
557
582
|
// Nạp luôn vào memory.
|
|
558
583
|
await this.session.replaceMemory(`[Quy tắc dự án từ winter.md]`, template);
|
|
@@ -577,13 +602,13 @@ export class WinterREPL {
|
|
|
577
602
|
if (!isWinterGeneratedProjectDoc(existing)) continue;
|
|
578
603
|
|
|
579
604
|
await fsPromises.writeFile(filePath, doc.content, 'utf8');
|
|
580
|
-
|
|
605
|
+
this.startupNotice(`updated ${doc.filename}`);
|
|
581
606
|
const memoryKey = `[Quy tắc dự án từ ${doc.filename}]`;
|
|
582
607
|
await this.session.replaceMemory(memoryKey, doc.content);
|
|
583
608
|
} catch {
|
|
584
609
|
try {
|
|
585
610
|
await fsPromises.writeFile(filePath, doc.content, 'utf8');
|
|
586
|
-
|
|
611
|
+
this.startupNotice(`created ${doc.filename}`);
|
|
587
612
|
const memoryKey = `[Quy tắc dự án từ ${doc.filename}]`;
|
|
588
613
|
await this.session.replaceMemory(memoryKey, doc.content);
|
|
589
614
|
} catch (err) {
|
|
@@ -596,51 +621,19 @@ export class WinterREPL {
|
|
|
596
621
|
await this.compactStartupMemories({ projectInstructionFiles, autoCreateDocs });
|
|
597
622
|
|
|
598
623
|
// Codebase Index: warm in background, then inject summaries into model context on demand.
|
|
599
|
-
this.codebaseWarmup = this.ensureCodebaseIndex({ verbose:
|
|
624
|
+
this.codebaseWarmup = this.ensureCodebaseIndex({ verbose: false })
|
|
600
625
|
.then(() => this.startCodebaseWatcher())
|
|
601
626
|
.catch((error) => {
|
|
602
|
-
|
|
627
|
+
this.startupNotice(`codebase disabled: ${error.message}`);
|
|
603
628
|
});
|
|
604
629
|
|
|
605
|
-
const activeProvider = this.ai.getActiveProvider();
|
|
606
|
-
const info = {
|
|
607
|
-
project: this.projectPath,
|
|
608
|
-
provider: activeProvider,
|
|
609
|
-
model: this.ai.providers[activeProvider]?.model,
|
|
610
|
-
session: this.session.getSessionId().substring(0, 8)
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
// Show banner only if not already shown
|
|
614
|
-
if (!process.env.WINTER_BANNER_SHOWN) {
|
|
615
|
-
console.log(welcomeBanner(this.version, info));
|
|
616
|
-
this.showCommandMenu();
|
|
617
|
-
process.env.WINTER_BANNER_SHOWN = '1';
|
|
618
|
-
} else {
|
|
619
|
-
this.showStatus();
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Hiển thị lịch sử chat nếu đang load lại session cũ, nhưng chỉ replay bản tóm tắt.
|
|
623
630
|
const sessionHistory = this.session.getHistory(4);
|
|
624
631
|
if (sessionHistory.length > 0) {
|
|
625
|
-
|
|
626
|
-
const W = Math.max(60, Math.min(Math.floor(columns * 0.95), 100));
|
|
627
|
-
const titleStr = ' Lịch sử phiên làm việc ';
|
|
628
|
-
const ruleChar = this.useUnicodeUi ? '─' : '-';
|
|
629
|
-
const sideLine = ruleChar.repeat(Math.max(0, Math.floor((W - titleStr.length) / 2)));
|
|
630
|
-
const bottomLine = ruleChar.repeat(W);
|
|
631
|
-
|
|
632
|
-
console.log(`\n${colors.dim}${sideLine}${titleStr}${sideLine}${colors.reset}`);
|
|
633
|
-
for (const msg of sessionHistory) {
|
|
634
|
-
const text = this.formatStartupHistoryEntry(msg.content);
|
|
635
|
-
if (msg.role === 'user') {
|
|
636
|
-
console.log(`\n${colors.cyan}Bạn:${colors.reset} ${text}`);
|
|
637
|
-
} else if (msg.role === 'assistant') {
|
|
638
|
-
console.log(`\n${colors.bright}${colors.magenta}Winter:${colors.reset} ${text}`);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
console.log(`\n${colors.dim}${bottomLine}${colors.reset}\n`);
|
|
632
|
+
this.startupNotice(`${sessionHistory.length} recent messages`);
|
|
642
633
|
}
|
|
643
634
|
|
|
635
|
+
this.showStatus();
|
|
636
|
+
|
|
644
637
|
// Setup readline
|
|
645
638
|
this.rl = readline.createInterface({
|
|
646
639
|
input: process.stdin,
|
|
@@ -711,16 +704,39 @@ export class WinterREPL {
|
|
|
711
704
|
return this.inputController.closeInputBox();
|
|
712
705
|
}
|
|
713
706
|
|
|
707
|
+
closeSlashMenu() {
|
|
708
|
+
return this.inputController.closeSlashMenu();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
handleSlashMenuKey(key = {}) {
|
|
712
|
+
return this.inputController.handleSlashMenuKey(key);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
handleDirectClipboardPaste() {
|
|
716
|
+
return this.inputController.handleDirectClipboardPaste();
|
|
717
|
+
}
|
|
718
|
+
|
|
714
719
|
buildInputPanel() {
|
|
715
720
|
return this.inputController.buildInputPanel();
|
|
716
721
|
}
|
|
717
722
|
|
|
718
723
|
showStatus() {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
+
const snapshot = buildTuiSnapshot(this);
|
|
725
|
+
if (process.stdout.isTTY) {
|
|
726
|
+
console.log(`\n${welcomeBanner(this.version, {
|
|
727
|
+
project: snapshot.projectPath,
|
|
728
|
+
session: snapshot.sessionShort,
|
|
729
|
+
provider: snapshot.provider,
|
|
730
|
+
model: snapshot.model,
|
|
731
|
+
})}\n`);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
console.log(`\n${renderConversationStartup(snapshot, { colors })}\n`);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
showTuiDashboard() {
|
|
738
|
+
const snapshot = buildTuiSnapshot(this);
|
|
739
|
+
console.log(`\n${renderLandingTui(snapshot, { colors, title: 'Winter Dashboard' })}\n`);
|
|
724
740
|
}
|
|
725
741
|
|
|
726
742
|
getResourcePaths() {
|
|
@@ -917,7 +933,8 @@ export class WinterREPL {
|
|
|
917
933
|
async handleInput(input) {
|
|
918
934
|
if (this.isProcessing) {
|
|
919
935
|
const pos = this.taskQueue.length + 1;
|
|
920
|
-
|
|
936
|
+
const preview = input.length > 40 ? input.slice(0, 37) + '...' : input;
|
|
937
|
+
console.log(`${colors.yellow}⧗${colors.reset} ${colors.bright}Queued #${pos}${colors.reset} ${colors.dim}› ${preview}${colors.reset}`);
|
|
921
938
|
this.taskQueue.push(input);
|
|
922
939
|
return;
|
|
923
940
|
}
|
|
@@ -927,7 +944,9 @@ export class WinterREPL {
|
|
|
927
944
|
async processInputTask(input) {
|
|
928
945
|
this.isProcessing = true;
|
|
929
946
|
this.isCancelled = false;
|
|
947
|
+
this.currentAbortController = new AbortController();
|
|
930
948
|
try {
|
|
949
|
+
this.closeSlashMenu();
|
|
931
950
|
this.history.push(input);
|
|
932
951
|
if (this.history.length > this.maxHistory) {
|
|
933
952
|
this.history = this.history.slice(-this.maxHistory);
|
|
@@ -998,13 +1017,16 @@ export class WinterREPL {
|
|
|
998
1017
|
}
|
|
999
1018
|
} catch (error) {
|
|
1000
1019
|
if (error.message === 'AbortError') {
|
|
1001
|
-
|
|
1020
|
+
if (!this.isCancelled) {
|
|
1021
|
+
console.log(colors.red + '\nĐã hủy công việc hiện tại.' + colors.reset);
|
|
1022
|
+
}
|
|
1002
1023
|
} else {
|
|
1003
1024
|
console.log(colors.red + '\nLỗi: ' + error.message + colors.reset);
|
|
1004
1025
|
}
|
|
1005
1026
|
} finally {
|
|
1006
1027
|
this.isProcessing = false;
|
|
1007
1028
|
this.isCancelled = false;
|
|
1029
|
+
this.currentAbortController = null;
|
|
1008
1030
|
if (this.spinner) this.spinner.stop();
|
|
1009
1031
|
|
|
1010
1032
|
if (this.taskQueue.length > 0) {
|
|
@@ -1277,60 +1299,13 @@ CRITICAL DEBUG/AGENT RULES:
|
|
|
1277
1299
|
showCommandMenu() {
|
|
1278
1300
|
const c = colors;
|
|
1279
1301
|
const width = terminalWidth(72, 112, 92);
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
if (!right) return left;
|
|
1285
|
-
return `${padVisible(left, split)} ${padVisible(right, rightWidth)}`;
|
|
1286
|
-
};
|
|
1287
|
-
|
|
1288
|
-
const body = [
|
|
1289
|
-
`${c.bright}${c.cyan}${this.useUnicodeUi ? '❄ ' : ''}WINTER COMMANDS${c.reset}`,
|
|
1290
|
-
`${c.dim}@file context | @Agent task | !cmd bash | /theme:toggle${c.reset}`,
|
|
1291
|
-
'',
|
|
1292
|
-
`${c.bright}Dự án & Phiên làm việc${c.reset}`,
|
|
1293
|
-
row(`${c.yellow}/pwd${c.reset} Thư mục hiện tại`, `${c.yellow}/session${c.reset} Phiên làm việc`),
|
|
1294
|
-
row(`${c.yellow}/cd${c.reset} Đổi thư mục`, `${c.yellow}/clear${c.reset} Xóa màn hình`),
|
|
1295
|
-
row(`${c.yellow}/config${c.reset} Xem cấu hình`, `${c.yellow}/exit${c.reset} Thoát`),
|
|
1296
|
-
'',
|
|
1297
|
-
`${c.bright}AI & Công cụ${c.reset}`,
|
|
1298
|
-
row(`${c.yellow}/auto${c.reset} TDD tự sửa lỗi`, `${c.yellow}/debug${c.reset} Auto debug lỗi`),
|
|
1299
|
-
row(`${c.yellow}/doctor${c.reset} Kiểm tra tool-call`, `${c.yellow}/agent${c.reset} Chạy sub-agent`),
|
|
1300
|
-
row(`${c.yellow}/swe${c.reset} SWE workflow`, `${c.yellow}/plan${c.reset} Lập kế hoạch`),
|
|
1301
|
-
row(`${c.yellow}/read${c.reset} Đọc file`, `${c.yellow}/write${c.reset} Ghi file`),
|
|
1302
|
-
row(`${c.yellow}/bash${c.reset} Chạy lệnh terminal`, `${c.yellow}/grep${c.reset} Tìm trong file`),
|
|
1303
|
-
row(`${c.yellow}/glob${c.reset} Tìm file theo pattern`, `${c.yellow}/image${c.reset} Ảnh/file/clipboard`),
|
|
1304
|
-
row(`${c.yellow}/paste${c.reset} Dán text/ảnh clipboard`, `${c.yellow}/composer${c.reset} Multi-file edit`),
|
|
1305
|
-
row(`${c.yellow}/complete${c.reset} Gợi ý code`, `${c.yellow}/search${c.reset} Tìm kiếm code`),
|
|
1306
|
-
row(`${c.yellow}/browse${c.reset} Mở URL trong trình duyệt`, `${c.yellow}/page-agent${c.reset} GUI Agent resources`),
|
|
1307
|
-
row(`${c.yellow}/ensemble${c.reset} Chạy nhiều AI`, `${c.yellow}/vote${c.reset} Bình chọn hay nhất`),
|
|
1308
|
-
row(`${c.yellow}/orchestrate${c.reset} Pipeline đa model`, `${c.yellow}/undo${c.reset} Undo backup`),
|
|
1309
|
-
row(`${c.yellow}/ecc${c.reset} ECC resource browser`, `${c.yellow}/codex${c.reset} Codex resources`),
|
|
1310
|
-
'',
|
|
1311
|
-
`${c.bright}Git Auto-Pilot${c.reset}`,
|
|
1312
|
-
row(`${c.yellow}/commit${c.reset} AI tự viết commit`, `${c.yellow}/review${c.reset} AI review code thay đổi`),
|
|
1313
|
-
'',
|
|
1314
|
-
`${c.bright}Cấu hình Model${c.reset}`,
|
|
1315
|
-
row(`${c.yellow}/provider${c.reset} Đổi provider AI`, `${c.yellow}/model${c.reset} Đổi model`),
|
|
1316
|
-
row(`${c.yellow}/providers${c.reset} Danh sách provider`, `${c.yellow}/models${c.reset} Danh sách model`),
|
|
1317
|
-
row(`${c.yellow}/mcp${c.reset} MCP server mgmt`, `${c.yellow}/permissions${c.reset} Quyền/allowlist`),
|
|
1318
|
-
'',
|
|
1319
|
-
`${c.bright}Bộ nhớ & Kỹ năng${c.reset}`,
|
|
1320
|
-
row(`${c.yellow}/remember${c.reset} Lưu vào bộ nhớ`, `${c.yellow}/memories${c.reset} Xem bộ nhớ`),
|
|
1321
|
-
row(`${c.yellow}/skills${c.reset} Danh sách kỹ năng`, `${c.yellow}/designs${c.reset} Hệ thống thiết kế`),
|
|
1322
|
-
];
|
|
1323
|
-
|
|
1324
|
-
console.log(`
|
|
1325
|
-
${renderBox({
|
|
1326
|
-
title: `${c.bright}${c.cyan}WINTER COMMANDS${c.reset}`,
|
|
1302
|
+
const snapshot = buildTuiSnapshot(this);
|
|
1303
|
+
console.log(`\n${renderLandingTui(snapshot, {
|
|
1304
|
+
colors: c,
|
|
1305
|
+
title: 'Winter Agent Console',
|
|
1327
1306
|
width,
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
body,
|
|
1331
|
-
})}
|
|
1332
|
-
${c.dim}Gửi tin nhắn trực tiếp để chat, ESC để hủy${c.reset}
|
|
1333
|
-
`);
|
|
1307
|
+
})}`);
|
|
1308
|
+
console.log(`${c.dim}Type ${c.cyan}/${c.dim} for palette, ${c.cyan}/help${c.dim} for the full command list.${c.reset}\n`);
|
|
1334
1309
|
}
|
|
1335
1310
|
|
|
1336
1311
|
showHelp() {
|
|
@@ -1500,13 +1475,42 @@ ${colors.reset}
|
|
|
1500
1475
|
}
|
|
1501
1476
|
|
|
1502
1477
|
getActiveModelTier() {
|
|
1478
|
+
if (typeof this.ai?._modelTier === 'string' && this.ai._modelTier) {
|
|
1479
|
+
return this.ai._modelTier;
|
|
1480
|
+
}
|
|
1503
1481
|
const providerName = this.ai?.getActiveProvider?.();
|
|
1504
1482
|
const model = this.ai?.providers?.[providerName]?.model || '';
|
|
1505
1483
|
return classifyModelTier(model, providerName);
|
|
1506
1484
|
}
|
|
1507
1485
|
|
|
1486
|
+
getBudgetScale(modelTier = this.getActiveModelTier()) {
|
|
1487
|
+
return getModelBudgetMultiplier(modelTier);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
getProjectContextBudget(modelTier = this.getActiveModelTier()) {
|
|
1491
|
+
return Math.round(6000 * this.getBudgetScale(modelTier));
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
getCodebaseContextBudget(modelTier = this.getActiveModelTier()) {
|
|
1495
|
+
return Math.round(4200 * this.getBudgetScale(modelTier));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
getTokenJuiceInlineBudget(modelTier = this.getActiveModelTier()) {
|
|
1499
|
+
return Math.max(800, Math.round(1400 * this.getBudgetScale(modelTier)));
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
getTokenJuiceForModelTier(modelTier = this.getActiveModelTier()) {
|
|
1503
|
+
const tokenJuice = new TokenJuice({
|
|
1504
|
+
projectPath: this.projectPath,
|
|
1505
|
+
inlineBudgetTokens: this.getTokenJuiceInlineBudget(modelTier),
|
|
1506
|
+
});
|
|
1507
|
+
this.tokenJuice = tokenJuice;
|
|
1508
|
+
return tokenJuice;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1508
1511
|
shouldUseCompactPrompt() {
|
|
1509
|
-
|
|
1512
|
+
const tier = this.getActiveModelTier();
|
|
1513
|
+
return tier === 'tiny' || tier === 'small';
|
|
1510
1514
|
}
|
|
1511
1515
|
|
|
1512
1516
|
selectExecutionProfile(messages = [], options = {}) {
|
|
@@ -1614,17 +1618,52 @@ ${colors.reset}
|
|
|
1614
1618
|
].join('\n');
|
|
1615
1619
|
}
|
|
1616
1620
|
|
|
1621
|
+
withCurrentAbortSignal(options = {}) {
|
|
1622
|
+
const signal = options.signal || options.abortSignal || this.currentAbortController?.signal;
|
|
1623
|
+
return signal ? { ...options, signal } : options;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
isAbortError(error) {
|
|
1627
|
+
return error?.name === 'AbortError' || error?.message === 'AbortError';
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
isRateLimitError(error) {
|
|
1631
|
+
const message = String(error?.message || error || '');
|
|
1632
|
+
return error?.status === 429 || /\b429\b|rate[_ -]?limit|tokens per minute|\bTPM\b/i.test(message);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
isTimeoutError(error) {
|
|
1636
|
+
const message = String(error?.message || error || '');
|
|
1637
|
+
return error?.name === 'TimeoutError'
|
|
1638
|
+
|| error?.code === 'ETIMEDOUT'
|
|
1639
|
+
|| /timed out|timeout|request aborted/i.test(message);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
cancelCurrentTask() {
|
|
1643
|
+
if (this.isCancelled) return;
|
|
1644
|
+
this.isCancelled = true;
|
|
1645
|
+
if (this.spinner) this.spinner.stop();
|
|
1646
|
+
if (this.currentAbortController && !this.currentAbortController.signal.aborted) {
|
|
1647
|
+
this.currentAbortController.abort(new DOMException('The operation was aborted.', 'AbortError'));
|
|
1648
|
+
}
|
|
1649
|
+
console.log(`\n\x1b[31m[ Đã hủy công việc hiện tại ]\x1b[0m`);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1617
1652
|
async requestAssistantTurn(messages, options, startedAt, totalUsage) {
|
|
1653
|
+
const requestOptions = this.withCurrentAbortSignal(options);
|
|
1618
1654
|
if (typeof this.ai.streamRequest === 'function') {
|
|
1619
1655
|
try {
|
|
1620
|
-
const streamed = await this.collectAssistantStream(messages,
|
|
1656
|
+
const streamed = await this.collectAssistantStream(messages, requestOptions, startedAt, totalUsage);
|
|
1621
1657
|
if (streamed) return streamed;
|
|
1622
1658
|
} catch (error) {
|
|
1659
|
+
if (this.isAbortError(error)) throw new Error('AbortError');
|
|
1660
|
+
if (this.isRateLimitError(error)) throw error;
|
|
1661
|
+
if (this.isTimeoutError(error)) throw error;
|
|
1623
1662
|
console.log(`${colors.dim}Streaming failed, retrying normal response: ${error.message}${colors.reset}`);
|
|
1624
1663
|
}
|
|
1625
1664
|
}
|
|
1626
1665
|
|
|
1627
|
-
const response = await this.ai.sendRequest(messages,
|
|
1666
|
+
const response = await this.ai.sendRequest(messages, requestOptions);
|
|
1628
1667
|
this.addUsage(totalUsage, response.usage);
|
|
1629
1668
|
const assistantMsg = response.choices?.[0]?.message || {};
|
|
1630
1669
|
const inlineToolExtraction = this.extractInlineToolCalls(assistantMsg.content || '');
|
|
@@ -1783,198 +1822,6 @@ ${colors.reset}
|
|
|
1783
1822
|
process.stdout.write(colors.reset);
|
|
1784
1823
|
}
|
|
1785
1824
|
|
|
1786
|
-
installSlashSuggestions() {
|
|
1787
|
-
if (!process.stdin.isTTY) return;
|
|
1788
|
-
|
|
1789
|
-
readline.emitKeypressEvents(process.stdin, this.rl);
|
|
1790
|
-
|
|
1791
|
-
process.stdin.on('keypress', (str, key = {}) => {
|
|
1792
|
-
if (key.ctrl && key.name === 'v') {
|
|
1793
|
-
void this.handleDirectClipboardPaste();
|
|
1794
|
-
return;
|
|
1795
|
-
}
|
|
1796
|
-
if (key.ctrl || key.meta) return;
|
|
1797
|
-
|
|
1798
|
-
if (typeof str === 'string' && str.length > 1) {
|
|
1799
|
-
return;
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
if (this.slashMenu.open && this.handleSlashMenuKey(key)) {
|
|
1803
|
-
return;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
if (key.name === 'escape' && this.isProcessing) {
|
|
1807
|
-
this.isCancelled = true;
|
|
1808
|
-
if (this.spinner) this.spinner.stop();
|
|
1809
|
-
console.log(`\n\x1b[31m[ Đã nhận lệnh HỦY... AI sẽ kết thúc ở thao tác tiếp theo ]\x1b[0m`);
|
|
1810
|
-
return;
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
queueMicrotask(() => {
|
|
1814
|
-
const line = this.rl?.line || '';
|
|
1815
|
-
if (!line.startsWith('/')) {
|
|
1816
|
-
this.closeSlashMenu();
|
|
1817
|
-
return;
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
this.openSlashMenu(line);
|
|
1821
|
-
});
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
async handleDirectClipboardPaste() {
|
|
1826
|
-
if (this._handlingDirectClipboardPaste || this.readlineClosed || !this.running) return false;
|
|
1827
|
-
this._handlingDirectClipboardPaste = true;
|
|
1828
|
-
try {
|
|
1829
|
-
const image = await this.getClipboardImage();
|
|
1830
|
-
if (!image) return false;
|
|
1831
|
-
|
|
1832
|
-
const prompt = (this.rl?.line || '').trim() || 'Analyze this pasted clipboard image.';
|
|
1833
|
-
this.closeSlashMenu();
|
|
1834
|
-
if (this.rl?.write) {
|
|
1835
|
-
this.rl.write(null, { ctrl: true, name: 'u' });
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
this.inputQueue = this.inputQueue
|
|
1839
|
-
.then(async () => {
|
|
1840
|
-
this.closeInputBox();
|
|
1841
|
-
await this.processPastedImageTask(prompt, image);
|
|
1842
|
-
})
|
|
1843
|
-
.catch((error) => {
|
|
1844
|
-
this.closeInputBox();
|
|
1845
|
-
console.log(`\n${colors.red}✖ Paste image error: ${error.message}${colors.reset}\n`);
|
|
1846
|
-
if (this.running && !this.readlineClosed) this.showInputPrompt();
|
|
1847
|
-
});
|
|
1848
|
-
return true;
|
|
1849
|
-
} finally {
|
|
1850
|
-
this._handlingDirectClipboardPaste = false;
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
async processPastedImageTask(prompt, image) {
|
|
1855
|
-
this.isProcessing = true;
|
|
1856
|
-
this.isCancelled = false;
|
|
1857
|
-
try {
|
|
1858
|
-
await this.chat(prompt, [image]);
|
|
1859
|
-
} finally {
|
|
1860
|
-
this.isProcessing = false;
|
|
1861
|
-
if (this.taskQueue.length > 0) {
|
|
1862
|
-
const nextTask = this.taskQueue.shift();
|
|
1863
|
-
setTimeout(() => this.processInputTask(nextTask), 0);
|
|
1864
|
-
} else if (!this.readlineClosed) {
|
|
1865
|
-
this.showInputPrompt();
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
openSlashMenu(line) {
|
|
1871
|
-
const matches = this.getSlashSuggestions(line);
|
|
1872
|
-
if (matches.length === 0) {
|
|
1873
|
-
this.closeSlashMenu();
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
if (this.slashMenu.open && this.slashMenu.line === line) return;
|
|
1877
|
-
|
|
1878
|
-
this.slashMenu = { open: true, line, items: matches, selected: 0, printedLines: this.slashMenu?.printedLines || 0 };
|
|
1879
|
-
this.renderSlashMenu();
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
closeSlashMenu() {
|
|
1883
|
-
if (this.slashMenu && this.slashMenu.printedLines) {
|
|
1884
|
-
readline.moveCursor(process.stdout, 0, -this.slashMenu.printedLines);
|
|
1885
|
-
readline.clearScreenDown(process.stdout);
|
|
1886
|
-
}
|
|
1887
|
-
this.slashMenu = { open: false, line: '', items: [], selected: 0, printedLines: 0 };
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
handleSlashMenuKey(key = {}) {
|
|
1891
|
-
if (key.name === 'up') {
|
|
1892
|
-
this.moveSlashSelection(-1);
|
|
1893
|
-
return true;
|
|
1894
|
-
}
|
|
1895
|
-
if (key.name === 'down') {
|
|
1896
|
-
this.moveSlashSelection(1);
|
|
1897
|
-
return true;
|
|
1898
|
-
}
|
|
1899
|
-
if (key.name === 'tab') {
|
|
1900
|
-
this.acceptSlashSelection();
|
|
1901
|
-
return true;
|
|
1902
|
-
}
|
|
1903
|
-
if (key.name === 'escape') {
|
|
1904
|
-
this.closeSlashMenu();
|
|
1905
|
-
this.rl.prompt(true);
|
|
1906
|
-
return true;
|
|
1907
|
-
}
|
|
1908
|
-
return false;
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
moveSlashSelection(delta) {
|
|
1912
|
-
if (!this.slashMenu.items.length) return;
|
|
1913
|
-
const count = this.slashMenu.items.length;
|
|
1914
|
-
this.slashMenu.selected = (this.slashMenu.selected + delta + count) % count;
|
|
1915
|
-
this.renderSlashMenu();
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
acceptSlashSelection() {
|
|
1919
|
-
const item = this.slashMenu.items[this.slashMenu.selected];
|
|
1920
|
-
if (!item) return;
|
|
1921
|
-
|
|
1922
|
-
const currentLine = String(this.rl?.line ?? this.slashMenu.line ?? '');
|
|
1923
|
-
const slashPrefixMatch = currentLine.match(/^\s*(\/\S*)(.*)$/);
|
|
1924
|
-
const prefix = slashPrefixMatch ? slashPrefixMatch[1] : currentLine.trim();
|
|
1925
|
-
const suffixText = slashPrefixMatch ? slashPrefixMatch[2] : '';
|
|
1926
|
-
const needsSpace = item.usage && suffixText && !/^\s/.test(suffixText);
|
|
1927
|
-
const replacement = `${item.cmd}${needsSpace ? ' ' : ''}${suffixText}`.trimEnd();
|
|
1928
|
-
|
|
1929
|
-
this.rl.write(null, { ctrl: true, name: 'u' });
|
|
1930
|
-
this.rl.write(replacement || prefix || item.cmd);
|
|
1931
|
-
this.closeSlashMenu();
|
|
1932
|
-
this.rl.prompt(true);
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
renderSlashMenu() {
|
|
1936
|
-
const matches = this.slashMenu.items;
|
|
1937
|
-
if (!matches.length) return;
|
|
1938
|
-
|
|
1939
|
-
if (this.slashMenu.printedLines) {
|
|
1940
|
-
readline.moveCursor(process.stdout, 0, -this.slashMenu.printedLines);
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
process.stdout.write('\n');
|
|
1944
|
-
readline.clearLine(process.stdout, 1);
|
|
1945
|
-
process.stdout.write(`${colors.dim}Commands${colors.reset}\n`);
|
|
1946
|
-
|
|
1947
|
-
const maxDisplay = 5;
|
|
1948
|
-
const displayedMatches = matches.slice(0, maxDisplay);
|
|
1949
|
-
|
|
1950
|
-
displayedMatches.forEach((item, index) => {
|
|
1951
|
-
readline.clearLine(process.stdout, 1);
|
|
1952
|
-
const usage = item.usage ? ` ${colors.dim}${item.usage}${colors.reset}` : '';
|
|
1953
|
-
const pointer = index === this.slashMenu.selected ? `${colors.green}>${colors.reset}` : ' ';
|
|
1954
|
-
process.stdout.write(`${pointer} ${colors.cyan}${item.cmd}${colors.reset} ${colors.dim}${item.desc}${colors.reset}${usage}\n`);
|
|
1955
|
-
});
|
|
1956
|
-
|
|
1957
|
-
if (matches.length > maxDisplay) {
|
|
1958
|
-
readline.clearLine(process.stdout, 1);
|
|
1959
|
-
process.stdout.write(` ${colors.dim}... và ${matches.length - maxDisplay} lệnh khác (gõ tiếp để lọc)${colors.reset}\n`);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
readline.clearLine(process.stdout, 1);
|
|
1963
|
-
process.stdout.write(`${colors.dim}↑/↓ chọn · Enter/Tab dùng · Esc đóng${colors.reset}\n`);
|
|
1964
|
-
|
|
1965
|
-
// Xóa các dòng thừa nếu số lượng dòng mới ít hơn số lượng dòng cũ.
|
|
1966
|
-
const currentLines = Math.min(matches.length, maxDisplay) + 3 + (matches.length > maxDisplay ? 1 : 0);
|
|
1967
|
-
if (this.slashMenu.printedLines > currentLines) {
|
|
1968
|
-
for (let i = 0; i < this.slashMenu.printedLines - currentLines; i++) {
|
|
1969
|
-
readline.clearLine(process.stdout, 1);
|
|
1970
|
-
process.stdout.write('\n');
|
|
1971
|
-
}
|
|
1972
|
-
readline.moveCursor(process.stdout, 0, -(this.slashMenu.printedLines - currentLines));
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
this.slashMenu.printedLines = currentLines;
|
|
1976
|
-
this.rl.prompt(true);
|
|
1977
|
-
}
|
|
1978
1825
|
|
|
1979
1826
|
getSlashSuggestions(line) {
|
|
1980
1827
|
const query = String(line || '').trim();
|
|
@@ -1984,7 +1831,7 @@ ${colors.reset}
|
|
|
1984
1831
|
const preferred = [
|
|
1985
1832
|
'/help', '/new', '/history', '/exit', '/pwd', '/cd',
|
|
1986
1833
|
'/provider', '/model', '/models', '/providers',
|
|
1987
|
-
'/theme:toggle',
|
|
1834
|
+
'/theme:toggle', '/tui',
|
|
1988
1835
|
'/auto', '/debug', '/doctor', '/context', '/scorecard', '/swe',
|
|
1989
1836
|
'/read', '/write', '/glob', '/grep', '/bash',
|
|
1990
1837
|
'/codex', '/claude', '/karpathy', '/agents',
|
|
@@ -2106,6 +1953,7 @@ ${colors.reset}
|
|
|
2106
1953
|
provider: executionProfile.provider,
|
|
2107
1954
|
model: executionProfile.model,
|
|
2108
1955
|
enableTools: false,
|
|
1956
|
+
signal: this.currentAbortController?.signal,
|
|
2109
1957
|
});
|
|
2110
1958
|
this.addUsage(totalUsage, response.usage);
|
|
2111
1959
|
const content = response.choices?.[0]?.message?.content || '';
|
|
@@ -2114,6 +1962,7 @@ ${colors.reset}
|
|
|
2114
1962
|
}
|
|
2115
1963
|
return content;
|
|
2116
1964
|
} catch (error) {
|
|
1965
|
+
if (this.isAbortError(error)) throw new Error('AbortError');
|
|
2117
1966
|
const fallback = this.buildToolFallbackAnswer(toolSummaries, error.message);
|
|
2118
1967
|
console.log(`\n${colors.yellow}${fallback}${colors.reset}\n`);
|
|
2119
1968
|
return fallback;
|
|
@@ -2131,6 +1980,7 @@ ${colors.reset}
|
|
|
2131
1980
|
provider: profile.provider,
|
|
2132
1981
|
model: profile.model,
|
|
2133
1982
|
enableTools: false,
|
|
1983
|
+
signal: this.currentAbortController?.signal,
|
|
2134
1984
|
})) {
|
|
2135
1985
|
if (chunk.usage) this.addUsage(totalUsage, chunk.usage);
|
|
2136
1986
|
if (chunk.content) {
|
|
@@ -2146,6 +1996,9 @@ ${colors.reset}
|
|
|
2146
1996
|
}
|
|
2147
1997
|
} catch (error) {
|
|
2148
1998
|
process.stdout.write(colors.reset);
|
|
1999
|
+
if (this.isAbortError(error)) throw new Error('AbortError');
|
|
2000
|
+
if (this.isRateLimitError(error)) throw error;
|
|
2001
|
+
if (this.isTimeoutError(error)) throw error;
|
|
2149
2002
|
console.log(`${colors.dim}Streaming failed, retrying normal response: ${error.message}${colors.reset}`);
|
|
2150
2003
|
}
|
|
2151
2004
|
|
|
@@ -2153,6 +2006,7 @@ ${colors.reset}
|
|
|
2153
2006
|
provider: profile.provider,
|
|
2154
2007
|
model: profile.model,
|
|
2155
2008
|
enableTools: false,
|
|
2009
|
+
signal: this.currentAbortController?.signal,
|
|
2156
2010
|
});
|
|
2157
2011
|
this.addUsage(totalUsage, response.usage);
|
|
2158
2012
|
content = response.choices?.[0]?.message?.content || '';
|
|
@@ -2165,17 +2019,13 @@ ${colors.reset}
|
|
|
2165
2019
|
printAssistantAnswer(content, startedAt, usage = {}) {
|
|
2166
2020
|
const formatted = formatMarkdown(content);
|
|
2167
2021
|
const footer = this.formatAnswerFooter(startedAt, usage);
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2022
|
+
console.log(`\n${renderAssistantPanel({
|
|
2023
|
+
content: formatted,
|
|
2024
|
+
footer,
|
|
2025
|
+
colors,
|
|
2171
2026
|
width: terminalWidth(72, 120, 92),
|
|
2172
|
-
borderColor: colors.blue,
|
|
2173
|
-
titleColor: colors.cyan,
|
|
2174
|
-
body: [...body, '', `${colors.dim}${footer}${colors.reset}`],
|
|
2175
2027
|
})}\n`);
|
|
2176
2028
|
return;
|
|
2177
|
-
console.log(`${colors.dim}${(this.useUnicodeUi ? '─' : '-').repeat(50)}${colors.reset}`);
|
|
2178
|
-
console.log(`${colors.dim}${this.formatAnswerFooter(startedAt, usage)}${colors.reset}\n`);
|
|
2179
2029
|
}
|
|
2180
2030
|
|
|
2181
2031
|
formatAnswerFooter(startedAt, usage = {}) {
|
|
@@ -2223,16 +2073,29 @@ ${colors.reset}
|
|
|
2223
2073
|
}
|
|
2224
2074
|
|
|
2225
2075
|
async promptToolPermission(commandText) {
|
|
2226
|
-
const
|
|
2227
|
-
const
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2076
|
+
const c = colors;
|
|
2077
|
+
const width = terminalWidth(68, 100, 80);
|
|
2078
|
+
const body = [
|
|
2079
|
+
`${c.yellow}${this.useUnicodeUi ? '⚠' : '!'} AI wants to run${c.reset}`,
|
|
2080
|
+
`${c.bright}${c.white}${commandText}${c.reset}`,
|
|
2081
|
+
'',
|
|
2082
|
+
`${c.cyan}1.${c.reset} Allow once`,
|
|
2083
|
+
`${c.cyan}2.${c.reset} Allow for session`,
|
|
2084
|
+
`${c.cyan}3.${c.reset} Deny`,
|
|
2085
|
+
];
|
|
2086
|
+
|
|
2087
|
+
console.log(renderBox({
|
|
2088
|
+
title: 'Tool Permission',
|
|
2089
|
+
width,
|
|
2090
|
+
borderColor: c.magenta,
|
|
2091
|
+
titleColor: c.yellow,
|
|
2092
|
+
body,
|
|
2093
|
+
boxChars: { topLeft: '+', topRight: '+', bottomLeft: '+', bottomRight: '+', horizontal: '-', vertical: '|', teeLeft: '+', teeRight: '+' },
|
|
2094
|
+
}));
|
|
2232
2095
|
|
|
2233
2096
|
while (true) {
|
|
2234
2097
|
const answer = await new Promise(resolve => {
|
|
2235
|
-
this.rl.question(`${
|
|
2098
|
+
this.rl.question(`${c.yellow}Choice [1/2/3]: ${c.reset}`, resolve);
|
|
2236
2099
|
});
|
|
2237
2100
|
|
|
2238
2101
|
const choice = String(answer || '').trim().toLowerCase();
|
|
@@ -2246,7 +2109,7 @@ ${colors.reset}
|
|
|
2246
2109
|
return false;
|
|
2247
2110
|
}
|
|
2248
2111
|
|
|
2249
|
-
|
|
2112
|
+
console.log(`${c.dim}Please choose 1, 2, or 3.${c.reset}`);
|
|
2250
2113
|
}
|
|
2251
2114
|
}
|
|
2252
2115
|
|
|
@@ -2479,6 +2342,7 @@ ${colors.reset}
|
|
|
2479
2342
|
}
|
|
2480
2343
|
|
|
2481
2344
|
} catch (error) {
|
|
2345
|
+
if (this.isAbortError(error)) throw error;
|
|
2482
2346
|
console.log(`\n${colors.red}✖ Error: ${error.message}${colors.reset}\n`);
|
|
2483
2347
|
}
|
|
2484
2348
|
}
|
|
@@ -2858,6 +2722,7 @@ Do NOT stop until all errors are resolved.`;
|
|
|
2858
2722
|
}
|
|
2859
2723
|
|
|
2860
2724
|
async getProjectContext(task = '') {
|
|
2725
|
+
const modelTier = this.getActiveModelTier();
|
|
2861
2726
|
const context = [];
|
|
2862
2727
|
const requiredLocalResources = await this.getRequiredLocalResourceSummary();
|
|
2863
2728
|
if (requiredLocalResources) {
|
|
@@ -2924,7 +2789,7 @@ Do NOT stop until all errors are resolved.`;
|
|
|
2924
2789
|
// Not a git repo or git not installed
|
|
2925
2790
|
}
|
|
2926
2791
|
|
|
2927
|
-
return this.compactText(context.join('\n\n') || 'No project context found.',
|
|
2792
|
+
return this.compactText(context.join('\n\n') || 'No project context found.', this.getProjectContextBudget(modelTier), 'project context');
|
|
2928
2793
|
}
|
|
2929
2794
|
|
|
2930
2795
|
async getLocalResourceContext() {
|
|
@@ -3026,7 +2891,7 @@ Do NOT stop until all errors are resolved.`;
|
|
|
3026
2891
|
this.promptBuilder.tools = this.tools;
|
|
3027
2892
|
this.promptBuilder.sessionPermissionGrants = this.sessionPermissionGrants;
|
|
3028
2893
|
return this.promptBuilder.buildSystemPrompt(context, {
|
|
3029
|
-
|
|
2894
|
+
modelTier: this.getActiveModelTier(),
|
|
3030
2895
|
});
|
|
3031
2896
|
}
|
|
3032
2897
|
|