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