ys-code-agent 2.0.1 → 2.0.2

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/dist/ui/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import { getLogger } from '../logger/index.js';
3
3
  import { configManager } from '../config/index.js';
4
- import { phoneConfig } from './phoneOptimizer.js';
5
4
  import { CommandPopup, CATEGORY_ICONS } from './CommandPopup.js';
6
5
  import { generateWelcome } from './WelcomeScreen.js';
6
+ import { createInterface, cursorTo, clearLine } from 'readline';
7
7
  const logger = getLogger('ui');
8
8
  const INDICATORS = {
9
9
  idle: '○',
@@ -27,24 +27,18 @@ export class TUI {
27
27
  status = 'idle';
28
28
  approvalMode = 'normal';
29
29
  agentMode = 'chat';
30
- outputLines = [];
31
- maxOutputLines = 1000;
32
30
  onInput = null;
33
- onSpecialKey = null;
31
+ onCancelRequest = null;
34
32
  running = false;
35
33
  commandPopup;
36
- inputBuffer = '';
37
34
  inputHistory = [];
38
35
  historyIndex = -1;
39
- cursorPos = 0;
40
36
  popupActive = false;
41
37
  cancelRequested = false;
42
- onCancelRequest = null;
43
- stdinRaw = false;
38
+ rl = null;
44
39
  constructor() {
45
40
  this.commandPopup = new CommandPopup();
46
41
  this.loadHistory();
47
- this.setupRawMode();
48
42
  }
49
43
  loadHistory() {
50
44
  try {
@@ -69,347 +63,91 @@ export class TUI {
69
63
  }
70
64
  catch { }
71
65
  }
72
- setupRawMode() {
73
- if (!process.stdin.isTTY)
74
- return;
75
- try {
76
- process.stdin.setRawMode?.(true);
77
- this.stdinRaw = true;
78
- }
79
- catch {
80
- this.stdinRaw = false;
81
- }
82
- }
83
66
  setOnCancelRequest(cb) {
84
67
  this.onCancelRequest = cb;
85
68
  }
86
69
  start() {
87
70
  this.running = true;
88
- this.showPrompt();
89
- this.startKeyListener();
90
- }
91
- startKeyListener() {
92
71
  if (!process.stdin.isTTY) {
93
72
  this.startLineMode();
94
73
  return;
95
74
  }
96
- process.stdin.on('data', (data) => {
97
- if (!this.running)
98
- return;
99
- const input = data.toString('utf-8');
100
- if (this.popupActive) {
101
- this.handlePopupKey(input);
102
- this.renderScreen();
103
- return;
104
- }
105
- if (input === '\x1b' || input === '\x1b[' || input === '\x1b[' && data.length > 2) {
106
- if (input === '\x1b') {
107
- if (this.inputBuffer.length === 0) {
108
- void this.handleSpecialKey('escape');
109
- }
110
- else {
111
- this.inputBuffer = '';
112
- this.cursorPos = 0;
113
- this.renderScreen();
114
- }
115
- return;
75
+ this.startTTYMode();
76
+ }
77
+ startTTYMode() {
78
+ const self = this;
79
+ this.rl = createInterface({
80
+ input: process.stdin,
81
+ output: process.stdout,
82
+ prompt: '',
83
+ historySize: 0,
84
+ removeHistoryDuplicates: true,
85
+ });
86
+ this.rl.on('line', async (line) => {
87
+ const trimmed = line.trim();
88
+ if (trimmed) {
89
+ this.addHistory(trimmed);
90
+ if (this.onInput) {
91
+ await this.onInput(trimmed);
116
92
  }
117
- return;
118
- }
119
- if (input === '\x1b[A') {
120
- void this.handleSpecialKey('up');
121
- return;
122
- }
123
- if (input === '\x1b[B') {
124
- void this.handleSpecialKey('down');
125
- return;
126
- }
127
- if (input === '\x1b[C') {
128
- void this.handleSpecialKey('right');
129
- return;
130
93
  }
131
- if (input === '\x1b[D') {
132
- void this.handleSpecialKey('left');
133
- return;
134
- }
135
- if (input === '\r' || input === '\n') {
136
- void this.handleSpecialKey('enter');
137
- return;
138
- }
139
- if (input === '\x7f' || input === '\b') {
140
- void this.handleSpecialKey('backspace');
141
- return;
142
- }
143
- if (input === '\x03') {
144
- void this.handleSpecialKey('ctrl_c');
145
- return;
146
- }
147
- if (input === '\x0c') {
148
- void this.handleSpecialKey('ctrl_l');
149
- return;
150
- }
151
- if (input === '\x01') {
152
- void this.handleSpecialKey('ctrl_a');
153
- return;
154
- }
155
- if (input === '\x05') {
156
- void this.handleSpecialKey('ctrl_e');
157
- return;
158
- }
159
- if (input === '\x0a' || input === '\x0d') {
160
- return;
161
- }
162
- if (input === '\x09') {
163
- void this.handleSpecialKey('tab');
164
- return;
165
- }
166
- if (input === '\x0f') {
167
- void this.handleSpecialKey('ctrl_o');
168
- return;
169
- }
170
- if (input === '\x18') {
171
- void this.handleSpecialKey('ctrl_x');
172
- return;
173
- }
174
- this.inputBuffer = this.inputBuffer.slice(0, this.cursorPos) + input + this.inputBuffer.slice(this.cursorPos);
175
- this.cursorPos += input.length;
176
- if (input === '/') {
177
- this.popupActive = true;
178
- this.commandPopup.open();
179
- this.commandPopup.setFilter(input);
94
+ this.showPrompt();
95
+ });
96
+ this.rl.on('SIGINT', () => {
97
+ if (this.cancelRequested || this.status === 'thinking' || this.status === 'executing') {
98
+ if (this.onCancelRequest)
99
+ this.onCancelRequest();
180
100
  }
181
101
  else {
182
- if (this.inputBuffer.startsWith('/') && this.inputBuffer.length > 1) {
183
- this.popupActive = true;
184
- this.commandPopup.open();
185
- this.commandPopup.setFilter(this.inputBuffer);
186
- }
102
+ process.stdout.write('\n');
103
+ this.printLine(chalk.yellow('Use /exit to quit, or press Ctrl+C again to force quit'));
104
+ this.cancelRequested = true;
105
+ setTimeout(() => { this.cancelRequested = false; }, 2000);
187
106
  }
188
- this.renderScreen();
107
+ this.showPrompt();
108
+ });
109
+ this.rl.on('close', () => {
110
+ process.exit(0);
189
111
  });
190
- process.stdin.on('end', () => { });
112
+ this.showPrompt();
191
113
  }
192
114
  startLineMode() {
193
- const { createInterface } = require('readline');
194
115
  const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: '' });
195
116
  rl.on('line', (line) => {
196
- this.inputBuffer = line.trim();
197
- if (this.inputBuffer && this.onInput) {
198
- this.addHistory(this.inputBuffer);
199
- this.onInput(this.inputBuffer);
117
+ const trimmed = line.trim();
118
+ if (trimmed && this.onInput) {
119
+ this.addHistory(trimmed);
120
+ this.onInput(trimmed);
200
121
  }
201
- this.inputBuffer = '';
202
- this.showPrompt();
122
+ rl.prompt();
203
123
  });
204
124
  rl.on('SIGINT', () => process.exit(0));
205
- this.showPrompt();
206
- }
207
- handlePopupKey(input) {
208
- if (input === '\x1b') {
209
- this.popupActive = false;
210
- this.commandPopup.close();
211
- this.inputBuffer = '';
212
- this.cursorPos = 0;
213
- return;
214
- }
215
- if (input === '\x1b[A' || input === '\x1bOA') {
216
- this.commandPopup.moveUp();
217
- return;
218
- }
219
- if (input === '\x1b[B' || input === '\x1bOB') {
220
- this.commandPopup.moveDown();
221
- return;
222
- }
223
- if (input === '\r' || input === '\n') {
224
- const selectedCmd = this.commandPopup.getSelectedCommand();
225
- if (selectedCmd) {
226
- this.popupActive = false;
227
- this.commandPopup.close();
228
- const fullCmd = `/${selectedCmd} `;
229
- this.inputBuffer = fullCmd;
230
- this.cursorPos = fullCmd.length;
231
- }
232
- return;
233
- }
234
- if (input === '\x09') {
235
- const selectedCmd = this.commandPopup.getSelectedCommand();
236
- if (selectedCmd) {
237
- this.popupActive = false;
238
- this.commandPopup.close();
239
- const fullCmd = `/${selectedCmd} `;
240
- this.inputBuffer = fullCmd;
241
- this.cursorPos = fullCmd.length;
242
- }
243
- return;
244
- }
245
- if (input === '\x7f' || input === '\b') {
246
- const currentFilter = this.commandPopup.getFilterText();
247
- if (currentFilter.length <= 1) {
248
- this.popupActive = false;
249
- this.commandPopup.close();
250
- this.inputBuffer = '';
251
- this.cursorPos = 0;
252
- return;
253
- }
254
- this.commandPopup.deleteChar();
255
- return;
256
- }
257
- if (input.length === 1 && input.charCodeAt(0) >= 32) {
258
- this.commandPopup.appendChar(input);
259
- this.inputBuffer = this.commandPopup.getFilterText();
260
- this.cursorPos = this.inputBuffer.length;
261
- }
262
- }
263
- async handleSpecialKey(key) {
264
- switch (key) {
265
- case 'escape':
266
- if (this.inputBuffer.length > 0) {
267
- this.inputBuffer = '';
268
- this.cursorPos = 0;
269
- this.renderScreen();
270
- }
271
- break;
272
- case 'enter':
273
- if (this.inputBuffer.trim()) {
274
- this.addHistory(this.inputBuffer.trim());
275
- const msg = this.inputBuffer.trim();
276
- this.inputBuffer = '';
277
- this.cursorPos = 0;
278
- this.clearOutputLine();
279
- if (this.onInput)
280
- this.onInput(msg);
281
- }
282
- this.renderScreen();
283
- break;
284
- case 'backspace':
285
- if (this.cursorPos > 0) {
286
- this.inputBuffer = this.inputBuffer.slice(0, this.cursorPos - 1) + this.inputBuffer.slice(this.cursorPos);
287
- this.cursorPos--;
288
- this.updatePopupFilter();
289
- }
290
- this.renderScreen();
291
- break;
292
- case 'up':
293
- if (this.inputHistory.length > 0) {
294
- if (this.historyIndex === -1) {
295
- this.historyIndex = this.inputHistory.length - 1;
296
- }
297
- else if (this.historyIndex > 0) {
298
- this.historyIndex--;
299
- }
300
- this.inputBuffer = this.inputHistory[this.historyIndex];
301
- this.cursorPos = this.inputBuffer.length;
302
- this.renderScreen();
303
- }
304
- break;
305
- case 'down':
306
- if (this.historyIndex >= 0) {
307
- this.historyIndex++;
308
- if (this.historyIndex >= this.inputHistory.length) {
309
- this.historyIndex = -1;
310
- this.inputBuffer = '';
311
- }
312
- else {
313
- this.inputBuffer = this.inputHistory[this.historyIndex];
314
- }
315
- this.cursorPos = this.inputBuffer.length;
316
- this.renderScreen();
317
- }
318
- break;
319
- case 'left':
320
- if (this.cursorPos > 0) {
321
- this.cursorPos--;
322
- this.renderScreen();
323
- }
324
- break;
325
- case 'right':
326
- if (this.cursorPos < this.inputBuffer.length) {
327
- this.cursorPos++;
328
- this.renderScreen();
329
- }
330
- break;
331
- case 'ctrl_a':
332
- this.cursorPos = 0;
333
- this.renderScreen();
334
- break;
335
- case 'ctrl_e':
336
- this.cursorPos = this.inputBuffer.length;
337
- this.renderScreen();
338
- break;
339
- case 'ctrl_c':
340
- if (this.cancelRequested || this.status === 'thinking' || this.status === 'executing') {
341
- if (this.onCancelRequest)
342
- this.onCancelRequest();
343
- }
344
- else {
345
- this.printLine('\n' + chalk.yellow('Use /exit to quit, or press Ctrl+C again to force quit'));
346
- this.cancelRequested = true;
347
- setTimeout(() => { this.cancelRequested = false; }, 2000);
348
- }
349
- break;
350
- case 'ctrl_l':
351
- this.clear();
352
- this.printWelcome();
353
- this.showPrompt();
354
- break;
355
- case 'tab': {
356
- const match = this.inputBuffer.match(/^\/?(\w*)$/);
357
- if (match) {
358
- const partial = match[1].toLowerCase();
359
- const cmds = this.getAllCommands().filter((c) => c.startsWith(partial));
360
- if (cmds.length === 1) {
361
- this.inputBuffer = `/${cmds[0]} `;
362
- this.cursorPos = this.inputBuffer.length;
363
- this.renderScreen();
364
- }
365
- }
366
- break;
367
- }
368
- case 'ctrl_o':
369
- break;
370
- case 'ctrl_x':
371
- void this.openExternalEditor();
372
- break;
373
- }
374
- }
375
- updatePopupFilter() {
376
- if (this.popupActive) {
377
- this.commandPopup.setFilter(this.inputBuffer);
378
- if (this.inputBuffer === '/' || this.inputBuffer === '') {
379
- this.popupActive = false;
380
- this.commandPopup.close();
381
- }
382
- }
125
+ rl.prompt();
383
126
  }
384
- getAllCommands() {
385
- const { ALL_COMMANDS } = require('./CommandPopup.js');
386
- return ALL_COMMANDS.map((c) => c.command);
127
+ buildPromptPrefix() {
128
+ const statusIndicator = STATUS_COLORS[this.status](INDICATORS[this.status]);
129
+ const config = configManager.getConfig();
130
+ const model = config.model.model;
131
+ const modelShort = model.length > 18 ? model.slice(0, 16) + '…' : model;
132
+ let modeBadge = '';
133
+ if (this.agentMode === 'plan')
134
+ modeBadge = chalk.blue(' [plan]');
135
+ else if (this.agentMode === 'goal')
136
+ modeBadge = chalk.magenta(' [goal]');
137
+ else if (this.agentMode === 'arena')
138
+ modeBadge = chalk.yellow(' [arena]');
139
+ let approvalBadge = '';
140
+ if (this.approvalMode === 'safe')
141
+ approvalBadge = chalk.cyan(' [safe]');
142
+ else if (this.approvalMode === 'yolo')
143
+ approvalBadge = chalk.red(' [yolo]');
144
+ return `${statusIndicator} ${chalk.cyan('ys')} ${chalk.yellow(`[${modelShort}]`)}${modeBadge}${approvalBadge} ${chalk.gray('›')} `;
387
145
  }
388
- async openExternalEditor() {
389
- const editor = process.env.EDITOR || 'nano';
390
- const { writeFileSync, unlinkSync, existsSync, mkdirSync } = await import('fs');
391
- const { join } = await import('path');
392
- const tmpDir = '/tmp/ys-agent';
393
- if (!existsSync(tmpDir))
394
- mkdirSync(tmpDir, { recursive: true });
395
- const tmpFile = join(tmpDir, `input-${Date.now()}.md`);
396
- writeFileSync(tmpFile, this.inputBuffer, 'utf-8');
397
- const { execSync } = await import('child_process');
398
- try {
399
- execSync(`${editor} "${tmpFile}"`, { stdio: 'inherit' });
400
- const { readFileSync } = await import('fs');
401
- const content = readFileSync(tmpFile, 'utf-8').trim();
402
- if (content) {
403
- this.inputBuffer = content;
404
- this.cursorPos = content.length;
405
- this.renderScreen();
406
- }
407
- }
408
- catch { }
409
- try {
410
- unlinkSync(tmpFile);
411
- }
412
- catch { }
146
+ showPrompt() {
147
+ if (!this.running || !this.rl)
148
+ return;
149
+ this.rl.setPrompt(this.buildPromptPrefix());
150
+ this.rl.prompt(true);
413
151
  }
414
152
  addHistory(input) {
415
153
  if (this.inputHistory[this.inputHistory.length - 1] !== input) {
@@ -418,53 +156,74 @@ export class TUI {
418
156
  this.historyIndex = -1;
419
157
  this.saveHistory();
420
158
  }
421
- renderScreen() {
422
- if (!process.stdout.isTTY)
423
- return;
424
- const lines = this.buildDisplayLines();
425
- const clearSeq = `\x1b[0J\x1b[${process.stdout.rows || 24};0H`;
426
- const output = clearSeq + lines.join('\n');
427
- try {
428
- cursorToSync(process.stdout, 0, (process.stdout.rows || 24) - lines.length);
429
- process.stdout.write(output);
430
- }
431
- catch { }
159
+ setOnInput(handler) {
160
+ this.onInput = handler;
432
161
  }
433
- buildDisplayLines() {
434
- const lines = [];
435
- if (this.popupActive) {
436
- const popupLines = this.commandPopup.render().split('\n');
437
- lines.push(...popupLines);
162
+ setStatus(status) {
163
+ this.status = status;
164
+ if (this.rl) {
165
+ this.rl.setPrompt(this.buildPromptPrefix());
438
166
  }
439
- lines.push(this.buildPromptLine());
440
- return lines;
441
167
  }
442
- stop() {
443
- this.running = false;
444
- if (this.stdinRaw && process.stdin.isTTY) {
445
- try {
446
- process.stdin.setRawMode?.(false);
447
- }
448
- catch { }
449
- }
450
- process.stdin.removeAllListeners('data');
451
- this.showShutdownScreen();
168
+ setApprovalMode(mode) {
169
+ this.approvalMode = mode;
452
170
  }
453
- clear() {
454
- console.clear();
171
+ setAgentMode(mode) {
172
+ this.agentMode = mode;
173
+ }
174
+ getApprovalMode() {
175
+ return this.approvalMode;
176
+ }
177
+ getAgentMode() {
178
+ return this.agentMode;
455
179
  }
456
- clearOutputLine() {
180
+ printLine(line) {
181
+ if (!process.stdout.isTTY) {
182
+ console.log(line);
183
+ return;
184
+ }
185
+ // Clear current line (hides partial user input), write output
457
186
  try {
458
- const { cursorTo, clearLine } = require('readline');
459
187
  cursorTo(process.stdout, 0);
460
188
  clearLine(process.stdout, 1);
189
+ process.stdout.write(line + '\n');
461
190
  }
462
- catch { }
191
+ catch {
192
+ console.log(line);
193
+ }
194
+ }
195
+ printAssistant(message) {
196
+ this.printLine(chalk.green(message));
197
+ }
198
+ printWarning(message) {
199
+ this.printLine(chalk.yellow(`⚠ ${message}`));
200
+ }
201
+ printError(error) {
202
+ const w = Math.max(Math.min(process.stdout.columns || 80, 56), 10);
203
+ this.printLine(chalk.red(`╔${'═'.repeat(w)}╗`));
204
+ this.printLine(chalk.red(`║`) + ` ${chalk.red('✗')} ${chalk.white(error)}${' '.repeat(Math.max(0, w - error.length - 7))}` + chalk.red(`║`));
205
+ this.printLine(chalk.red(`╚${'═'.repeat(w)}╝`));
206
+ }
207
+ printToolCall(toolName, args) {
208
+ const argsStr = Object.entries(args)
209
+ .slice(0, 3)
210
+ .map(([k, v]) => `${k}=${String(v).slice(0, 50)}`)
211
+ .join(', ');
212
+ this.printLine(chalk.gray(` ⚡ ${toolName}(${argsStr}${Object.keys(args).length > 3 ? ', ...' : ''})`));
213
+ }
214
+ printToolResult(result) {
215
+ if (result.success) {
216
+ this.printLine(chalk.gray(` ✓ ${chalk.green('success')}`));
217
+ }
218
+ else {
219
+ this.printLine(chalk.gray(` ✕ ${chalk.red(result.error || 'failed')}`));
220
+ }
221
+ }
222
+ showStatusBar(_info) {
463
223
  }
464
224
  printWelcome() {
465
225
  const welcome = generateWelcome();
466
- const lines = welcome.split('\n');
467
- for (const line of lines) {
226
+ for (const line of welcome.split('\n')) {
468
227
  this.printLine(line);
469
228
  }
470
229
  this.printLine('');
@@ -528,199 +287,23 @@ export class TUI {
528
287
  for (const l of lines)
529
288
  this.printLine(l);
530
289
  }
531
- printWarning(message) {
532
- const w = Math.max(Math.min(process.stdout.columns || 80, 60), 10);
533
- const top = `╔${'═'.repeat(w)}╗`;
534
- const bottom = `╚${'═'.repeat(w)}╝`;
535
- this.printLine('');
536
- this.printLine(chalk.yellow(top));
537
- const warnLine = ` ⚠ ${chalk.yellow(message)}`;
538
- const pad = Math.max(0, w - warnLine.length);
539
- this.printLine(`║${warnLine}${' '.repeat(pad)}║`);
540
- this.printLine(chalk.yellow(bottom));
541
- }
542
- showPrompt() {
543
- if (!this.running)
544
- return;
545
- const promptLine = this.buildPromptLine();
546
- if (phoneConfig.isTermux) {
547
- this.printLine(promptLine);
548
- }
549
- else {
550
- this.renderScreen();
551
- }
552
- }
553
- buildPromptLine() {
554
- const statusIndicator = STATUS_COLORS[this.status](INDICATORS[this.status]);
555
- const config = configManager.getConfig();
556
- const model = config.model.model;
557
- const modelShort = model.length > 18 ? model.slice(0, 16) + '…' : model;
558
- let modeBadge = '';
559
- if (this.agentMode === 'plan')
560
- modeBadge = chalk.blue(' [plan]');
561
- else if (this.agentMode === 'goal')
562
- modeBadge = chalk.magenta(' [goal]');
563
- else if (this.agentMode === 'arena')
564
- modeBadge = chalk.yellow(' [arena]');
565
- let approvalBadge = '';
566
- if (this.approvalMode === 'safe')
567
- approvalBadge = chalk.cyan(' [safe]');
568
- else if (this.approvalMode === 'yolo')
569
- approvalBadge = chalk.red(' [yolo]');
570
- const promptStr = `${statusIndicator} ${chalk.cyan('ys')} ${chalk.yellow(`[${modelShort}]`)}${modeBadge}${approvalBadge} ${chalk.gray('›')} `;
571
- const inputStr = this.inputBuffer || ' ';
572
- const cursorChar = phoneConfig.isTermux ? '█' : '█';
573
- const cursor = chalk.gray(cursorChar);
574
- const displayedInput = inputStr.length > 0 ? inputStr : '';
575
- let cursorLine = '';
576
- if (this.cursorPos >= displayedInput.length) {
577
- cursorLine = promptStr + displayedInput + cursor;
578
- }
579
- else {
580
- const before = displayedInput.slice(0, this.cursorPos);
581
- const after = displayedInput.slice(this.cursorPos);
582
- cursorLine = promptStr + before + cursor + after;
583
- }
584
- return cursorLine + '\n' + chalk.gray(` 💡 / for commands | ↑↓ history | Tab complete`);
585
- }
586
- setOnInput(handler) {
587
- this.onInput = handler;
588
- }
589
- setOnSpecialKey(handler) {
590
- this.onSpecialKey = handler;
591
- }
592
- setStatus(status) {
593
- this.status = status;
594
- }
595
- setApprovalMode(mode) {
596
- this.approvalMode = mode;
597
- }
598
- setAgentMode(mode) {
599
- this.agentMode = mode;
600
- }
601
- getApprovalMode() {
602
- return this.approvalMode;
603
- }
604
- getAgentMode() {
605
- return this.agentMode;
606
- }
607
- printLine(line) {
608
- if (!process.stdout.isTTY) {
609
- console.log(line);
610
- return;
611
- }
612
- try {
613
- const { cursorTo, clearLine } = require('readline');
614
- cursorTo(process.stdout, 0);
615
- clearLine(process.stdout, 1);
616
- console.log(line);
617
- }
618
- catch {
619
- console.log(line);
620
- }
621
- this.outputLines.push(line);
622
- if (this.outputLines.length > this.maxOutputLines) {
623
- this.outputLines.shift();
624
- }
625
- }
626
- printAssistant(message) {
627
- this.printLine('');
628
- const formatted = chalk.green(message);
629
- this.printLine(formatted);
630
- }
631
- printError(error) {
632
- const w = Math.max(Math.min(process.stdout.columns || 80, 56), 10);
633
- const top = `╔${'═'.repeat(w)}╗`;
634
- const bottom = `╚${'═'.repeat(w)}╝`;
635
- this.printLine('');
636
- this.printLine(chalk.red(top));
637
- this.printLine(`║ ${chalk.red('✗')} ${chalk.white(error)}${' '.repeat(Math.max(0, w - error.length - 7))}║`);
638
- this.printLine(chalk.red(bottom));
639
- }
640
- printToolCall(toolName, args) {
641
- const argsStr = Object.entries(args)
642
- .slice(0, 3)
643
- .map(([k, v]) => `${k}=${String(v).slice(0, 50)}`)
644
- .join(', ');
645
- this.printLine(chalk.gray(` ⚡ ${toolName}(${argsStr}${Object.keys(args).length > 3 ? ', ...' : ''})`));
646
- }
647
- printToolResult(result) {
648
- if (result.success) {
649
- this.printLine(chalk.gray(` ✓ ${chalk.green('success')}`));
650
- }
651
- else {
652
- this.printLine(chalk.gray(` ✕ ${chalk.red(result.error || 'failed')}`));
653
- }
654
- }
655
- showStatusBar(info) {
656
- const statusColor = STATUS_COLORS[info.status];
657
- const statusIcon = INDICATORS[info.status];
658
- const truncated = info.task ? info.task.slice(0, 40) + (info.task.length > 40 ? '...' : '') : '';
659
- const parts = [
660
- `${statusColor(`${statusIcon} ${info.status}`)}`,
661
- chalk.gray(`msgs:${info.messages}`),
662
- chalk.gray(`tok:${info.tokens}`),
663
- ];
664
- if (info.provider)
665
- parts.push(chalk.gray(`prov:${info.provider}`));
666
- if (truncated)
667
- parts.push(chalk.gray(`task:${truncated}`));
290
+ clear() {
291
+ console.clear();
668
292
  }
669
- showShutdownScreen() {
670
- const w = Math.max(Math.min(process.stdout.columns || 80, 50), 30);
671
- const top = `╭${'─'.repeat(w)}╮`;
672
- const bottom = `╰${'─'.repeat(w)}╯`;
673
- console.log('');
674
- console.log(chalk.cyan(top));
675
- console.log(`│${' '.repeat(w)}│`);
676
- const titleLine = ' ◆ YS Code Agent — Session Complete ';
677
- const titlePad = Math.max(0, Math.floor((w - titleLine.length) / 2));
678
- console.log(`│${' '.repeat(titlePad)}${chalk.cyan(titleLine)}${' '.repeat(Math.max(0, w - titlePad - titleLine.length))}│`);
679
- console.log(`│${' '.repeat(w)}│`);
680
- const { sessionManager } = require('../session/index.js');
681
- const session = sessionManager.getCurrentSession();
682
- if (session) {
683
- const sessionId = session.id.slice(0, 8);
684
- const duration = formatDuration(Date.now() - session.createdAt);
685
- const msgCount = session.state.messages.length;
686
- console.log(`│ ${chalk.white('Session')}: ${chalk.yellow(sessionId.padEnd(w - 13))}│`);
687
- console.log(`│ ${chalk.white('Duration')}: ${chalk.yellow(duration.padEnd(w - 14))}│`);
688
- console.log(`│ ${chalk.white('Messages')}: ${chalk.yellow(String(msgCount).padEnd(w - 14))}│`);
293
+ stop() {
294
+ this.running = false;
295
+ if (this.rl) {
296
+ this.rl.close();
297
+ this.rl = null;
689
298
  }
690
- console.log(`│${' '.repeat(w)}│`);
691
- console.log(`│ ${chalk.gray('Session saved. Resume with:')} ${chalk.yellow('ys --resume <session>')}${' '.repeat(Math.max(0, w - 43))}│`);
692
- console.log(`│${' '.repeat(w)}│`);
693
- console.log(chalk.cyan(bottom));
694
- console.log('');
299
+ process.stdin.removeAllListeners('data');
695
300
  }
696
301
  destroy() {
697
- if (this.stdinRaw && process.stdin.isTTY) {
698
- try {
699
- process.stdin.setRawMode?.(false);
700
- }
701
- catch { }
702
- }
703
- process.stdin.removeAllListeners('data');
302
+ this.stop();
704
303
  }
705
304
  getOutputLineCount() {
706
- return this.outputLines.length;
707
- }
708
- }
709
- function formatDuration(ms) {
710
- if (ms < 1000)
711
- return `${ms}ms`;
712
- if (ms < 60000)
713
- return `${(ms / 1000).toFixed(1)}s`;
714
- const m = Math.floor(ms / 60000);
715
- const s = Math.floor((ms % 60000) / 1000);
716
- return `${m}m ${s}s`;
717
- }
718
- function cursorToSync(stream, x, y) {
719
- try {
720
- const { cursorTo } = require('readline');
721
- cursorTo(stream, x, y);
305
+ return 0;
722
306
  }
723
- catch { }
724
307
  }
725
308
  export const tui = new TUI();
726
309
  //# sourceMappingURL=index.js.map