winter-super-cli 2026.6.5 → 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.
@@ -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
- bgCyan: '\x1b[46m',
21
+ brightGreen: '\x1b[92m',
17
22
  bgMagenta: '\x1b[45m',
18
23
  };
19
24
 
20
- const DARK_THEME = { cyan: '\x1b[36m', blue: '\x1b[34m', magenta: '\x1b[35m', white: '\x1b[37m', red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', dim: '\x1b[2m' };
21
- const LIGHT_THEME = { cyan: '\x1b[96m', blue: '\x1b[94m', magenta: '\x1b[95m', white: '\x1b[97m', red: '\x1b[91m', yellow: '\x1b[93m', green: '\x1b[92m', dim: '\x1b[2m' };
25
+ const DARK_THEME = { ...colors };
26
+ const LIGHT_THEME = { ...colors };
22
27
 
23
28
  export function applyColorTheme(theme = 'dark') {
24
- const selected = String(theme || '').toLowerCase() === 'light' ? LIGHT_THEME : DARK_THEME;
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 snowflakeArt = String.raw`
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 pPath = info.project || 'Unknown';
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
- // Tính toán chiều rộng động (nhỏ hơn 5% cửa sổ, tối thiểu 60, tối đa 100 cho đẹp)
64
- const columns = process.stdout.columns || 80;
65
- const W = Math.max(60, Math.min(Math.floor(columns * 0.95), 100));
66
- const unicode = supportsUnicodeUi();
67
- const dot = `${colors.green}${unicode ? '●' : '*'}${colors.reset}`;
68
-
69
- // Căn giữa Snowflake Art
70
- const artLines = snowflakeArt.split('\n').filter(l => l.trim() !== '' || l.length > 0);
71
- // Tìm chiều dài thực tế lớn nhất của art (bỏ qua khoảng trắng thừa ở cuối)
72
- const maxArtWidth = Math.max(...artLines.map(l => l.trimEnd().length));
73
- const artPadding = Math.max(0, Math.floor((W - maxArtWidth) / 2));
74
- const centeredArt = artLines.map(l => ' '.repeat(artPadding) + l.trimEnd()).join('\n');
75
-
76
- // Căn giữa tiêu đề
77
- const title = `W I N T E R v${version}`;
78
- const titlePadding = Math.max(0, Math.floor((W - title.length) / 2));
79
-
80
- const subtitle = `Build by Atus | fb: iam.anhtu | github: anhtu1707`;
81
- const subPadding = Math.max(0, Math.floor((W - subtitle.length) / 2));
82
-
83
- const infoWidth = Math.max(60, Math.min(terminalWidth(60, 100, 80), 100));
84
- const banner = `${colors.cyan}${centeredArt}${colors.reset}
85
-
86
- ${' '.repeat(titlePadding)}${colors.bright}${colors.magenta}W I N T E R${colors.reset} ${colors.dim}v${version}${colors.reset}
87
- ${' '.repeat(subPadding)}${colors.dim}${subtitle}${colors.reset}
88
- ${renderBox({
89
- title: '',
90
- width: infoWidth,
91
- borderColor: colors.blue,
92
- titleColor: colors.blue,
93
- body: [
94
- `${dot} ${colors.cyan}Project:${colors.reset} ${colors.green}${displayPath}${colors.reset}`,
95
- `${dot} ${colors.cyan}Model: ${colors.reset} ${model} ${colors.dim}(${provider})${colors.reset}`,
96
- `${dot} ${colors.cyan}Session:${colors.reset} ${colors.yellow}${pId}${colors.reset}`,
97
- `${colors.dim}Gõ ${colors.cyan}/help${colors.dim} để xem lệnh · ${colors.cyan}/auto${colors.dim} chế độ tự sửa · ${colors.cyan}ESC${colors.dim} để hủy${colors.reset}`,
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}${supportsUnicodeUi() ? '●' : 'on'}${colors.reset}`,
106
- offline: `${colors.dim}${supportsUnicodeUi() ? '○' : 'off'}${colors.reset}`,
107
- warning: `${colors.yellow}${supportsUnicodeUi() ? '◆' : '!'}${colors.reset}`,
108
- error: `${colors.red}${supportsUnicodeUi() ? '✖' : 'x'}${colors.reset}`,
109
- success: `${colors.green}${supportsUnicodeUi() ? '✓' : 'ok'}${colors.reset}`,
110
- thinking: `${colors.cyan}${supportsUnicodeUi() ? '◉' : '...'}${colors.reset}`,
111
- queue: `${colors.magenta}${supportsUnicodeUi() ? '◎' : 'queue'}${colors.reset}`,
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 sessionIndicator(sessionId) {
115
- const id = sessionId ? sessionId.substring(0, 8) : 'none';
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 providerStatus(name, status) {
120
- const icon = status === 'ready' ? statusIcons.online : statusIcons.offline;
121
- return `${icon} ${name}`;
98
+ export function sessionIndicator(sessionId) {
99
+ return `session:${sessionId}`;
122
100
  }
123
101
 
124
- export const snowflake = snowflakeArt;
102
+ export const snowflake = '';
@@ -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 true;
73
+ if (env.WINTER_UNICODE_UI === '1' || env.WINTER_UNICODE_UI === 'true') return platform !== 'win32';
74
74
  if (platform !== 'win32') return true;
75
- return Boolean(env.WT_SESSION || env.TERM_PROGRAM || env.TERM || env.ConEmuANSI === 'ON');
75
+ return false;
76
76
  }
77
77
 
78
78
  export function getBoxChars() {
79
- return supportsUnicodeUi() ? UNICODE_BOX : ASCII_BOX;
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
+
@@ -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
  });