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
|
@@ -18,6 +18,7 @@ export const SLASH_COMMANDS = [
|
|
|
18
18
|
{ cmd: '/compress', desc: 'Compress old conversation context' },
|
|
19
19
|
{ cmd: '/context', desc: 'Inspect what Winter sends to the model', usage: '/context [task]' },
|
|
20
20
|
{ cmd: '/scorecard', desc: 'Score Winter against Codebuff/Codex/Claude capability gates' },
|
|
21
|
+
{ cmd: '/tui', desc: 'Show Winter TUI dashboard' },
|
|
21
22
|
{ cmd: '/plan', desc: 'Create/view plans' },
|
|
22
23
|
{ cmd: '/plans', desc: 'List active plans' },
|
|
23
24
|
{ cmd: '/tasks', desc: 'List tasks' },
|
|
@@ -6,6 +6,11 @@ export const colors = {
|
|
|
6
6
|
dim: '\x1b[2m',
|
|
7
7
|
italic: '\x1b[3m',
|
|
8
8
|
underline: '\x1b[4m',
|
|
9
|
+
bgBlack: '\x1b[40m',
|
|
10
|
+
bgBlue: '\x1b[44m',
|
|
11
|
+
bgCyan: '\x1b[46m',
|
|
12
|
+
bgWhite: '\x1b[47m',
|
|
13
|
+
bgBrightBlue: '\x1b[104m',
|
|
9
14
|
cyan: '\x1b[36m',
|
|
10
15
|
blue: '\x1b[34m',
|
|
11
16
|
magenta: '\x1b[35m',
|
|
@@ -13,112 +18,85 @@ export const colors = {
|
|
|
13
18
|
red: '\x1b[31m',
|
|
14
19
|
yellow: '\x1b[33m',
|
|
15
20
|
green: '\x1b[32m',
|
|
16
|
-
|
|
21
|
+
brightGreen: '\x1b[92m',
|
|
17
22
|
bgMagenta: '\x1b[45m',
|
|
18
23
|
};
|
|
19
24
|
|
|
20
|
-
const DARK_THEME = {
|
|
21
|
-
const LIGHT_THEME = {
|
|
25
|
+
const DARK_THEME = { ...colors };
|
|
26
|
+
const LIGHT_THEME = { ...colors };
|
|
22
27
|
|
|
23
28
|
export function applyColorTheme(theme = 'dark') {
|
|
24
|
-
|
|
25
|
-
Object.assign(colors, selected, { theme: String(theme || 'dark').toLowerCase() === 'light' ? 'light' : 'dark' });
|
|
29
|
+
colors.theme = theme;
|
|
26
30
|
return colors.theme;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
const
|
|
30
|
-
ii
|
|
31
|
-
.i;
|
|
32
|
-
, ;i ,
|
|
33
|
-
;i::;:i;
|
|
34
|
-
,i1:
|
|
35
|
-
:,, ,, ;:,::i,;:i: , i ii;
|
|
36
|
-
,ii;;: ii ,1 :;iiiii: ;1 .1, i;:,
|
|
37
|
-
:::ii,,;1. ;iii; :1:,:i1;,
|
|
38
|
-
,;i:,i:i1:i, ,;i; ;i;1i;; :;:
|
|
39
|
-
,. ,i1iii11, ,;, ,11ii;ii;
|
|
40
|
-
:;: ::,,:i111i: ,, ,:
|
|
41
|
-
:: ,,, :i111i:,,,: :;:
|
|
42
|
-
:ii;ii11: ,;, 11iii1i:
|
|
43
|
-
:i: ;;i1;;; ;i;, ,i:1i:i,:i;:
|
|
44
|
-
,;1i;,:1; ;iiii 1;,,i1::,
|
|
45
|
-
,,;i, 1, i; :iiiii;: 1: ii ,;;ii,
|
|
46
|
-
;ii i, , :i::,;:,::;. ,: ,,:
|
|
47
|
-
:ii:
|
|
48
|
-
;i:;;:i;
|
|
49
|
-
;i
|
|
50
|
-
;i,
|
|
51
|
-
;i.
|
|
52
|
-
`;
|
|
53
|
-
|
|
54
|
-
export const miniLogo = `${colors.cyan}${supportsUnicodeUi() ? '❄' : '*'}${colors.reset}`;
|
|
33
|
+
export const miniLogo = `${colors.cyan}❄${colors.reset}`;
|
|
55
34
|
|
|
56
35
|
export function welcomeBanner(version, info = {}) {
|
|
57
|
-
const
|
|
58
|
-
const displayPath = pPath; // Hiển thị đầy đủ đường dẫn chính xác
|
|
59
|
-
const pId = info.session || 'New';
|
|
36
|
+
const displayPath = info.project || 'Unknown';
|
|
60
37
|
const provider = info.provider || 'default';
|
|
61
38
|
const model = info.model || 'unknown';
|
|
62
39
|
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
${
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
40
|
+
const W = Math.max(60, Math.min(process.stdout.columns || 80, 100));
|
|
41
|
+
const white = colors.white;
|
|
42
|
+
const dim = colors.dim;
|
|
43
|
+
const bright = colors.bright;
|
|
44
|
+
const reset = colors.reset;
|
|
45
|
+
const green = '\x1b[92m';
|
|
46
|
+
const cyan = '\x1b[36m';
|
|
47
|
+
const bgBrightBlue = '\x1b[104m';
|
|
48
|
+
const bgBlue = '\x1b[48;5;236m';
|
|
49
|
+
|
|
50
|
+
const logo = [
|
|
51
|
+
'╔══════════════════════════════════════════════════════════════════╗',
|
|
52
|
+
'║ ║',
|
|
53
|
+
'║ ██╗ ██╗██╗███╗ ██╗████████╗███████╗██████╗ ║',
|
|
54
|
+
'║ ██║ ██║██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗ ║',
|
|
55
|
+
'║ ██║ █╗ ██║██║██╔██╗██║ ██║ █████╗ ██████╔╝ ║',
|
|
56
|
+
'║ ██║███╗██║██║██║╚████║ ██║ ██╔══╝ ██╔══██╗ ║',
|
|
57
|
+
'║ ╚███╔███╔╝██║██║ ╚███║ ██║ ███████╗██║ ██║ ║',
|
|
58
|
+
'║ ╚══╝╚══╝ ╚═╝╚═╝ ╚══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║',
|
|
59
|
+
'║ ║',
|
|
60
|
+
'║ A I C o d i n g A s s i s t a n t ║',
|
|
61
|
+
'║══════════════════════════════════════════════════════════════════║',
|
|
62
|
+
'║ fb.com/iam.anhtu ║ github.com/anhtu1707 ║',
|
|
63
|
+
'╚══════════════════════════════════════════════════════════════════╝',
|
|
64
|
+
];
|
|
65
|
+
const logoLines = logo.map(line => `${bright}${cyan}${line}${reset}`);
|
|
66
|
+
|
|
67
|
+
const leftStatus = ` ${provider} · ${model} `;
|
|
68
|
+
const rightStatus = ` ESC×2 exit · /help `;
|
|
69
|
+
const padding = Math.max(0, W - leftStatus.length - rightStatus.length);
|
|
70
|
+
const statusBar = `${bgBlue}${white}${leftStatus}${' '.repeat(padding)}${rightStatus}${reset}`;
|
|
71
|
+
|
|
72
|
+
const banner = [
|
|
73
|
+
...logoLines,
|
|
74
|
+
'',
|
|
75
|
+
`${white}Winter will run commands on your behalf to help you build.${reset}`,
|
|
76
|
+
'',
|
|
77
|
+
`${white}Directory${reset} ${dim}${displayPath}${reset}`,
|
|
78
|
+
'',
|
|
79
|
+
statusBar
|
|
80
|
+
].join('\n');
|
|
101
81
|
return banner;
|
|
102
82
|
}
|
|
103
83
|
|
|
104
84
|
export const statusIcons = {
|
|
105
|
-
online: `${colors.green}
|
|
106
|
-
offline: `${colors.dim}
|
|
107
|
-
warning: `${colors.yellow}
|
|
108
|
-
error: `${colors.red}
|
|
109
|
-
success: `${colors.green}
|
|
110
|
-
thinking: `${colors.cyan}
|
|
111
|
-
queue: `${colors.magenta}
|
|
85
|
+
online: `${colors.green}●${colors.reset}`,
|
|
86
|
+
offline: `${colors.dim}○${colors.reset}`,
|
|
87
|
+
warning: `${colors.yellow}◆${colors.reset}`,
|
|
88
|
+
error: `${colors.red}✖${colors.reset}`,
|
|
89
|
+
success: `${colors.green}✓${colors.reset}`,
|
|
90
|
+
thinking: `${colors.cyan}◉${colors.reset}`,
|
|
91
|
+
queue: `${colors.magenta}◎${colors.reset}`,
|
|
112
92
|
};
|
|
113
93
|
|
|
114
|
-
export function
|
|
115
|
-
|
|
116
|
-
return `${colors.dim}[${colors.cyan}session:${id}${colors.dim}]${colors.reset}`;
|
|
94
|
+
export function providerStatus(name, status) {
|
|
95
|
+
return `${statusIcons[status] || statusIcons.offline} ${name}`;
|
|
117
96
|
}
|
|
118
97
|
|
|
119
|
-
export function
|
|
120
|
-
|
|
121
|
-
return `${icon} ${name}`;
|
|
98
|
+
export function sessionIndicator(sessionId) {
|
|
99
|
+
return `session:${sessionId}`;
|
|
122
100
|
}
|
|
123
101
|
|
|
124
|
-
export const snowflake =
|
|
102
|
+
export const snowflake = '';
|
package/src/cli/terminal-ui.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
2
2
|
const ZERO_WIDTH_PATTERN = /[\u200B-\u200D\u2060\uFE0E\uFE0F]/u;
|
|
3
|
-
const WIDE_CODE_POINT_RANGES = [
|
|
3
|
+
const WIDE_CODE_POINT_RANGES = [
|
|
4
4
|
[0x1100, 0x115f],
|
|
5
5
|
[0x2329, 0x232a],
|
|
6
6
|
[0x2e80, 0x303e],
|
|
@@ -14,30 +14,30 @@ const WIDE_CODE_POINT_RANGES = [
|
|
|
14
14
|
[0x1f300, 0x1f64f],
|
|
15
15
|
[0x1f680, 0x1f6ff],
|
|
16
16
|
[0x1f900, 0x1f9ff],
|
|
17
|
-
[0x1fa70, 0x1faff],
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const UNICODE_BOX = {
|
|
21
|
-
topLeft: '╭',
|
|
22
|
-
topRight: '╮',
|
|
23
|
-
bottomLeft: '╰',
|
|
24
|
-
bottomRight: '╯',
|
|
25
|
-
horizontal: '─',
|
|
26
|
-
vertical: '│',
|
|
27
|
-
teeLeft: '├',
|
|
28
|
-
teeRight: '┤',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const ASCII_BOX = {
|
|
32
|
-
topLeft: '+',
|
|
33
|
-
topRight: '+',
|
|
34
|
-
bottomLeft: '+',
|
|
35
|
-
bottomRight: '+',
|
|
36
|
-
horizontal: '-',
|
|
37
|
-
vertical: '|',
|
|
38
|
-
teeLeft: '+',
|
|
39
|
-
teeRight: '+',
|
|
40
|
-
};
|
|
17
|
+
[0x1fa70, 0x1faff],
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const UNICODE_BOX = {
|
|
21
|
+
topLeft: '╭',
|
|
22
|
+
topRight: '╮',
|
|
23
|
+
bottomLeft: '╰',
|
|
24
|
+
bottomRight: '╯',
|
|
25
|
+
horizontal: '─',
|
|
26
|
+
vertical: '│',
|
|
27
|
+
teeLeft: '├',
|
|
28
|
+
teeRight: '┤',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const ASCII_BOX = {
|
|
32
|
+
topLeft: '+',
|
|
33
|
+
topRight: '+',
|
|
34
|
+
bottomLeft: '+',
|
|
35
|
+
bottomRight: '+',
|
|
36
|
+
horizontal: '-',
|
|
37
|
+
vertical: '|',
|
|
38
|
+
teeLeft: '+',
|
|
39
|
+
teeRight: '+',
|
|
40
|
+
};
|
|
41
41
|
|
|
42
42
|
export function stripAnsi(text) {
|
|
43
43
|
return String(text ?? '').replace(ANSI_PATTERN, '');
|
|
@@ -63,20 +63,20 @@ export function charDisplayWidth(char) {
|
|
|
63
63
|
return 1;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function terminalWidth(min = 72, max = 120, fallback = 88) {
|
|
67
|
-
const columns = process.stdout.columns || fallback;
|
|
68
|
-
return Math.max(min, Math.min(columns - 2, max));
|
|
69
|
-
}
|
|
70
|
-
|
|
66
|
+
export function terminalWidth(min = 72, max = 120, fallback = 88) {
|
|
67
|
+
const columns = process.stdout.columns || fallback;
|
|
68
|
+
return Math.max(min, Math.min(columns - 2, max));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
71
|
export function supportsUnicodeUi(env = process.env, platform = process.platform) {
|
|
72
72
|
if (env.WINTER_ASCII_UI === '1' || env.WINTER_ASCII_UI === 'true') return false;
|
|
73
|
-
if (env.WINTER_UNICODE_UI === '1' || env.WINTER_UNICODE_UI === 'true') return
|
|
73
|
+
if (env.WINTER_UNICODE_UI === '1' || env.WINTER_UNICODE_UI === 'true') return platform !== 'win32';
|
|
74
74
|
if (platform !== 'win32') return true;
|
|
75
|
-
return
|
|
75
|
+
return false;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export function getBoxChars() {
|
|
79
|
-
return
|
|
79
|
+
return ASCII_BOX;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export function padVisible(text, width, fill = ' ') {
|
|
@@ -124,38 +124,38 @@ export function wrapText(text, width) {
|
|
|
124
124
|
return output;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
export function chunkText(text, width) {
|
|
128
|
-
const chars = Array.from(stripAnsi(text));
|
|
129
|
-
const chunks = [];
|
|
130
|
-
let current = '';
|
|
131
|
-
let currentWidth = 0;
|
|
132
|
-
for (const char of chars) {
|
|
133
|
-
const charWidth = charDisplayWidth(char);
|
|
134
|
-
if (current && currentWidth + charWidth > width) {
|
|
135
|
-
chunks.push(current);
|
|
136
|
-
current = '';
|
|
137
|
-
currentWidth = 0;
|
|
138
|
-
}
|
|
139
|
-
current += char;
|
|
140
|
-
currentWidth += charWidth;
|
|
141
|
-
}
|
|
142
|
-
if (current) chunks.push(current);
|
|
143
|
-
return chunks.length > 0 ? chunks : [''];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function renderBox({
|
|
147
|
-
title = '',
|
|
148
|
-
body = [],
|
|
149
|
-
width,
|
|
150
|
-
borderColor = '\x1b[35m',
|
|
151
|
-
titleColor = '\x1b[36m',
|
|
152
|
-
reset = '\x1b[0m',
|
|
153
|
-
boxChars = getBoxChars(),
|
|
154
|
-
} = {}) {
|
|
155
|
-
const innerWidth = Math.max(28, (width || terminalWidth()) - 4);
|
|
156
|
-
const top = `${borderColor}${boxChars.topLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.topRight}${reset}`;
|
|
157
|
-
const bottom = `${borderColor}${boxChars.bottomLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.bottomRight}${reset}`;
|
|
158
|
-
const lines = [];
|
|
127
|
+
export function chunkText(text, width) {
|
|
128
|
+
const chars = Array.from(stripAnsi(text));
|
|
129
|
+
const chunks = [];
|
|
130
|
+
let current = '';
|
|
131
|
+
let currentWidth = 0;
|
|
132
|
+
for (const char of chars) {
|
|
133
|
+
const charWidth = charDisplayWidth(char);
|
|
134
|
+
if (current && currentWidth + charWidth > width) {
|
|
135
|
+
chunks.push(current);
|
|
136
|
+
current = '';
|
|
137
|
+
currentWidth = 0;
|
|
138
|
+
}
|
|
139
|
+
current += char;
|
|
140
|
+
currentWidth += charWidth;
|
|
141
|
+
}
|
|
142
|
+
if (current) chunks.push(current);
|
|
143
|
+
return chunks.length > 0 ? chunks : [''];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function renderBox({
|
|
147
|
+
title = '',
|
|
148
|
+
body = [],
|
|
149
|
+
width,
|
|
150
|
+
borderColor = '\x1b[35m',
|
|
151
|
+
titleColor = '\x1b[36m',
|
|
152
|
+
reset = '\x1b[0m',
|
|
153
|
+
boxChars = getBoxChars(),
|
|
154
|
+
} = {}) {
|
|
155
|
+
const innerWidth = Math.max(28, (width || terminalWidth()) - 4);
|
|
156
|
+
const top = `${borderColor}${boxChars.topLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.topRight}${reset}`;
|
|
157
|
+
const bottom = `${borderColor}${boxChars.bottomLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.bottomRight}${reset}`;
|
|
158
|
+
const lines = [];
|
|
159
159
|
const titleText = title ? ` ${title} ` : '';
|
|
160
160
|
|
|
161
161
|
if (titleText) {
|
|
@@ -166,45 +166,99 @@ export function renderBox({
|
|
|
166
166
|
const padding = Math.max(0, innerWidth - visible);
|
|
167
167
|
const left = index === 0 ? Math.floor(padding / 2) : 0;
|
|
168
168
|
const right = index === 0 ? padding - left : padding;
|
|
169
|
-
lines.push(`${borderColor}${boxChars.vertical}${reset}${' '.repeat(left)}${titleColor}${plainSegment}${reset}${' '.repeat(right)}${borderColor}${boxChars.vertical}${reset}`);
|
|
170
|
-
});
|
|
171
|
-
lines.push(`${borderColor}${boxChars.teeLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.teeRight}${reset}`);
|
|
172
|
-
}
|
|
169
|
+
lines.push(`${borderColor}${boxChars.vertical}${reset}${' '.repeat(left)}${titleColor}${plainSegment}${reset}${' '.repeat(right)}${borderColor}${boxChars.vertical}${reset}`);
|
|
170
|
+
});
|
|
171
|
+
lines.push(`${borderColor}${boxChars.teeLeft}${boxChars.horizontal.repeat(innerWidth)}${boxChars.teeRight}${reset}`);
|
|
172
|
+
}
|
|
173
173
|
|
|
174
174
|
for (const item of body) {
|
|
175
175
|
const rawText = String(item ?? '');
|
|
176
176
|
if (visibleWidth(rawText) <= innerWidth) {
|
|
177
177
|
const visible = visibleWidth(rawText);
|
|
178
178
|
const padding = Math.max(0, innerWidth - visible);
|
|
179
|
-
lines.push(`${borderColor}${boxChars.vertical}${reset} ${rawText}${' '.repeat(Math.max(0, padding - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
179
|
+
lines.push(`${borderColor}${boxChars.vertical}${reset} ${rawText}${' '.repeat(Math.max(0, padding - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
182
|
|
|
183
183
|
const wrapped = wrapText(rawText, innerWidth);
|
|
184
184
|
if (wrapped.length === 0) {
|
|
185
|
-
lines.push(`${borderColor}${boxChars.vertical}${reset} ${' '.repeat(Math.max(0, innerWidth - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
185
|
+
lines.push(`${borderColor}${boxChars.vertical}${reset} ${' '.repeat(Math.max(0, innerWidth - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
188
|
|
|
189
189
|
for (const segment of wrapped) {
|
|
190
190
|
const text = stripAnsi(segment);
|
|
191
191
|
const visible = visibleWidth(text);
|
|
192
192
|
const padding = Math.max(0, innerWidth - visible);
|
|
193
|
-
lines.push(`${borderColor}${boxChars.vertical}${reset} ${text}${' '.repeat(Math.max(0, padding - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
193
|
+
lines.push(`${borderColor}${boxChars.vertical}${reset} ${text}${' '.repeat(Math.max(0, padding - 1))}${borderColor}${boxChars.vertical}${reset}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
196
|
|
|
197
197
|
return [top, ...lines, bottom].join('\n');
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
export function renderKeyValueRows(rows, width, colors) {
|
|
201
|
-
const innerWidth = Math.max(28, (width || terminalWidth()) - 4);
|
|
202
|
-
const boxChars = getBoxChars();
|
|
203
|
-
return rows.map(([left, right]) => {
|
|
200
|
+
export function renderKeyValueRows(rows, width, colors) {
|
|
201
|
+
const innerWidth = Math.max(28, (width || terminalWidth()) - 4);
|
|
202
|
+
const boxChars = getBoxChars();
|
|
203
|
+
return rows.map(([left, right]) => {
|
|
204
204
|
const leftWidth = Math.floor(innerWidth * 0.5);
|
|
205
205
|
const rightWidth = innerWidth - leftWidth - 1;
|
|
206
206
|
const leftText = padVisible(left, leftWidth);
|
|
207
207
|
const rightText = padVisible(right, rightWidth);
|
|
208
|
-
return `${colors.border}${boxChars.vertical}${colors.reset} ${leftText}${colors.spacer}${rightText} ${colors.border}${boxChars.vertical}${colors.reset}`;
|
|
209
|
-
});
|
|
210
|
-
}
|
|
208
|
+
return `${colors.border}${boxChars.vertical}${colors.reset} ${leftText}${colors.spacer}${rightText} ${colors.border}${boxChars.vertical}${colors.reset}`;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
export const PANEL_HEIGHT = 5;
|
|
215
|
+
|
|
216
|
+
let _fixedEnabled = false;
|
|
217
|
+
|
|
218
|
+
export function enableFixedPanel() {
|
|
219
|
+
if (!process.stdout.isTTY) return false;
|
|
220
|
+
_fixedEnabled = true;
|
|
221
|
+
const rows = process.stdout.rows || 24;
|
|
222
|
+
const scrollBottom = Math.max(1, rows - PANEL_HEIGHT);
|
|
223
|
+
process.stdout.write("\x1b[1;" + scrollBottom + "r");
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function disableFixedPanel() {
|
|
228
|
+
_fixedEnabled = false;
|
|
229
|
+
process.stdout.write("\x1b[r");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function refreshFixedPanel() {
|
|
233
|
+
if (!_fixedEnabled || !process.stdout.isTTY) return;
|
|
234
|
+
const rows = process.stdout.rows || 24;
|
|
235
|
+
const scrollBottom = Math.max(1, rows - PANEL_HEIGHT);
|
|
236
|
+
process.stdout.write("\x1b[1;" + scrollBottom + "r");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function drawInFixedArea(content) {
|
|
240
|
+
if (!_fixedEnabled || !process.stdout.isTTY) return;
|
|
241
|
+
const rows = process.stdout.rows || 24;
|
|
242
|
+
const startRow = Math.max(1, rows - PANEL_HEIGHT + 1);
|
|
243
|
+
process.stdout.write("\x1b7");
|
|
244
|
+
process.stdout.write("\x1b[" + startRow + ";1H");
|
|
245
|
+
process.stdout.write("\x1b[J");
|
|
246
|
+
process.stdout.write(String(content ?? ""));
|
|
247
|
+
process.stdout.write("\x1b8");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function moveToScrollRegion() {
|
|
251
|
+
if (!process.stdout.isTTY) return;
|
|
252
|
+
const rows = process.stdout.rows || 24;
|
|
253
|
+
const scrollBottom = Math.max(1, rows - PANEL_HEIGHT);
|
|
254
|
+
process.stdout.write("\x1b[" + scrollBottom + ";1H");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function moveToPromptRow() {
|
|
258
|
+
if (!process.stdout.isTTY) return;
|
|
259
|
+
const rows = process.stdout.rows || 24;
|
|
260
|
+
// Position prompt at last scrollable row (just above the fixed panel)
|
|
261
|
+
const promptRow = Math.max(1, rows - PANEL_HEIGHT - 1);
|
|
262
|
+
process.stdout.write("\x1b[" + promptRow + ";1H");
|
|
263
|
+
}
|
|
264
|
+
|
package/src/cli/tool-runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TokenJuice } from '../context/token-juice.js';
|
|
2
|
+
import { getModelBudgetMultiplier } from '../ai/model-capabilities.js';
|
|
2
3
|
|
|
3
4
|
export function getMutatingToolNames() {
|
|
4
5
|
return new Set([
|
|
@@ -18,7 +19,8 @@ export function getMutatingToolNames() {
|
|
|
18
19
|
]);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function getToolResultPromptBudget(toolName, compact = false) {
|
|
22
|
+
export function getToolResultPromptBudget(toolName, compact = false, modelTier = '') {
|
|
23
|
+
const scale = getModelBudgetMultiplier(modelTier);
|
|
22
24
|
const budgets = {
|
|
23
25
|
Read: compact ? 2800 : 5000,
|
|
24
26
|
Grep: compact ? 2200 : 4200,
|
|
@@ -29,19 +31,20 @@ export function getToolResultPromptBudget(toolName, compact = false) {
|
|
|
29
31
|
BrowserDebug: compact ? 2200 : 4200,
|
|
30
32
|
NotebookRead: compact ? 2600 : 5000,
|
|
31
33
|
};
|
|
32
|
-
return budgets[toolName] || (compact ? 1800 : 3200);
|
|
34
|
+
return Math.max(400, Math.round((budgets[toolName] || (compact ? 1800 : 3200)) * scale));
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export function buildPromptToolResult({
|
|
36
38
|
toolName,
|
|
37
39
|
result,
|
|
38
40
|
compact = false,
|
|
41
|
+
modelTier = '',
|
|
39
42
|
compactText = (value, maxChars) => String(value || '').slice(0, maxChars),
|
|
40
43
|
summarizeToolResult = value => ({ ...value }),
|
|
41
44
|
} = {}) {
|
|
42
45
|
if (!result || typeof result !== 'object') return result;
|
|
43
46
|
|
|
44
|
-
const budget = getToolResultPromptBudget(toolName, compact);
|
|
47
|
+
const budget = getToolResultPromptBudget(toolName, compact, modelTier);
|
|
45
48
|
const copy = summarizeToolResult(result);
|
|
46
49
|
const preserveKeys = ['content', 'stdout', 'stderr', 'diff', 'matches', 'files', 'cells'];
|
|
47
50
|
let remaining = budget;
|
|
@@ -78,6 +81,7 @@ export async function buildPromptToolResultWithTokenJuice({
|
|
|
78
81
|
projectPath = process.cwd(),
|
|
79
82
|
tokenJuice,
|
|
80
83
|
compact = false,
|
|
84
|
+
modelTier = '',
|
|
81
85
|
compactText = (value, maxChars) => String(value || '').slice(0, maxChars),
|
|
82
86
|
summarizeToolResult = value => ({ ...value }),
|
|
83
87
|
} = {}) {
|
|
@@ -85,6 +89,7 @@ export async function buildPromptToolResultWithTokenJuice({
|
|
|
85
89
|
toolName,
|
|
86
90
|
result,
|
|
87
91
|
compact,
|
|
92
|
+
modelTier,
|
|
88
93
|
compactText,
|
|
89
94
|
summarizeToolResult,
|
|
90
95
|
});
|