ys-code-agent 2.0.1 → 3.0.0
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/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +26 -18
- package/dist/agent/index.js.map +1 -1
- package/dist/cli/index.cjs +440 -529
- package/dist/cli/index.cjs.map +4 -4
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.js +3 -3
- package/dist/cli/interactive.js.map +1 -1
- package/dist/providers/anthropic.d.ts +0 -4
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +2 -24
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.d.ts +3 -1
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/base.js +54 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/ui/CommandPopup.d.ts.map +1 -1
- package/dist/ui/CommandPopup.js +3 -7
- package/dist/ui/CommandPopup.js.map +1 -1
- package/dist/ui/index.d.ts +20 -26
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +352 -498
- package/dist/ui/index.js.map +1 -1
- package/dist/utils/renderMarkdown.d.ts +2 -0
- package/dist/utils/renderMarkdown.d.ts.map +1 -0
- package/dist/utils/renderMarkdown.js +18 -0
- package/dist/utils/renderMarkdown.js.map +1 -0
- package/package.json +1 -1
package/dist/ui/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
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 { renderMarkdown } from '../utils/renderMarkdown.js';
|
|
7
|
+
import { cursorTo, clearLine, moveCursor, emitKeypressEvents } from 'readline';
|
|
7
8
|
const logger = getLogger('ui');
|
|
8
9
|
const INDICATORS = {
|
|
9
10
|
idle: '○',
|
|
@@ -27,24 +28,21 @@ export class TUI {
|
|
|
27
28
|
status = 'idle';
|
|
28
29
|
approvalMode = 'normal';
|
|
29
30
|
agentMode = 'chat';
|
|
30
|
-
outputLines = [];
|
|
31
|
-
maxOutputLines = 1000;
|
|
32
31
|
onInput = null;
|
|
33
|
-
|
|
32
|
+
onCancelRequest = null;
|
|
34
33
|
running = false;
|
|
35
34
|
commandPopup;
|
|
36
|
-
inputBuffer = '';
|
|
37
35
|
inputHistory = [];
|
|
38
36
|
historyIndex = -1;
|
|
39
|
-
cursorPos = 0;
|
|
40
37
|
popupActive = false;
|
|
41
38
|
cancelRequested = false;
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
inputBuffer = '';
|
|
40
|
+
cursorPos = 0;
|
|
41
|
+
popupLineCount = 0;
|
|
42
|
+
prevInput = null;
|
|
44
43
|
constructor() {
|
|
45
44
|
this.commandPopup = new CommandPopup();
|
|
46
45
|
this.loadHistory();
|
|
47
|
-
this.setupRawMode();
|
|
48
46
|
}
|
|
49
47
|
loadHistory() {
|
|
50
48
|
try {
|
|
@@ -69,340 +67,280 @@ export class TUI {
|
|
|
69
67
|
}
|
|
70
68
|
catch { }
|
|
71
69
|
}
|
|
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
70
|
setOnCancelRequest(cb) {
|
|
84
71
|
this.onCancelRequest = cb;
|
|
85
72
|
}
|
|
86
73
|
start() {
|
|
87
74
|
this.running = true;
|
|
88
|
-
this.showPrompt();
|
|
89
|
-
this.startKeyListener();
|
|
90
|
-
}
|
|
91
|
-
startKeyListener() {
|
|
92
75
|
if (!process.stdin.isTTY) {
|
|
93
76
|
this.startLineMode();
|
|
94
77
|
return;
|
|
95
78
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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;
|
|
116
|
-
}
|
|
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
|
-
}
|
|
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;
|
|
79
|
+
this.startTTYMode();
|
|
80
|
+
}
|
|
81
|
+
startLineMode() {
|
|
82
|
+
const { createInterface } = require('readline');
|
|
83
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: '' });
|
|
84
|
+
rl.on('line', (line) => {
|
|
85
|
+
const trimmed = line.trim();
|
|
86
|
+
if (trimmed && this.onInput) {
|
|
87
|
+
this.addHistory(trimmed);
|
|
88
|
+
this.onInput(trimmed);
|
|
142
89
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
90
|
+
rl.prompt();
|
|
91
|
+
});
|
|
92
|
+
rl.on('SIGINT', () => process.exit(0));
|
|
93
|
+
rl.prompt();
|
|
94
|
+
}
|
|
95
|
+
startTTYMode() {
|
|
96
|
+
try {
|
|
97
|
+
process.stdin.setRawMode?.(true);
|
|
98
|
+
}
|
|
99
|
+
catch { }
|
|
100
|
+
emitKeypressEvents(process.stdin);
|
|
101
|
+
this.inputBuffer = '';
|
|
102
|
+
this.cursorPos = 0;
|
|
103
|
+
this.popupLineCount = 0;
|
|
104
|
+
process.stdin.on('keypress', this.handleKeypress.bind(this));
|
|
105
|
+
this.renderScreen();
|
|
106
|
+
}
|
|
107
|
+
async handleKeypress(str, key) {
|
|
108
|
+
if (!this.running)
|
|
109
|
+
return;
|
|
110
|
+
if (!key)
|
|
111
|
+
key = {};
|
|
112
|
+
const k = key.name || '';
|
|
113
|
+
const c = key.ctrl || false;
|
|
114
|
+
if (this.popupActive) {
|
|
115
|
+
await this.handlePopupKeypress(str, key);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (k === 'escape') {
|
|
119
|
+
if (this.inputBuffer.length > 0) {
|
|
120
|
+
this.inputBuffer = '';
|
|
121
|
+
this.cursorPos = 0;
|
|
146
122
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
123
|
+
this.renderScreen();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (k === 'return' || k === 'enter') {
|
|
127
|
+
const input = this.inputBuffer.trim();
|
|
128
|
+
this.inputBuffer = '';
|
|
129
|
+
this.cursorPos = 0;
|
|
130
|
+
if (input) {
|
|
131
|
+
this.addHistory(input);
|
|
132
|
+
if (this.onInput) {
|
|
133
|
+
await this.onInput(input);
|
|
134
|
+
}
|
|
150
135
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
136
|
+
this.renderScreen();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (k === 'backspace') {
|
|
140
|
+
if (this.cursorPos > 0) {
|
|
141
|
+
this.inputBuffer = this.inputBuffer.slice(0, this.cursorPos - 1) + this.inputBuffer.slice(this.cursorPos);
|
|
142
|
+
this.cursorPos--;
|
|
154
143
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
this.renderScreen();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (k === 'up') {
|
|
148
|
+
if (this.inputHistory.length > 0) {
|
|
149
|
+
if (this.historyIndex === -1) {
|
|
150
|
+
this.historyIndex = this.inputHistory.length - 1;
|
|
151
|
+
}
|
|
152
|
+
else if (this.historyIndex > 0) {
|
|
153
|
+
this.historyIndex--;
|
|
154
|
+
}
|
|
155
|
+
this.inputBuffer = this.inputHistory[this.historyIndex];
|
|
156
|
+
this.cursorPos = this.inputBuffer.length;
|
|
158
157
|
}
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
this.renderScreen();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (k === 'down') {
|
|
162
|
+
if (this.historyIndex >= 0) {
|
|
163
|
+
this.historyIndex++;
|
|
164
|
+
if (this.historyIndex >= this.inputHistory.length) {
|
|
165
|
+
this.historyIndex = -1;
|
|
166
|
+
this.inputBuffer = '';
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.inputBuffer = this.inputHistory[this.historyIndex];
|
|
170
|
+
}
|
|
171
|
+
this.cursorPos = this.inputBuffer.length;
|
|
161
172
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
this.renderScreen();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (k === 'left') {
|
|
177
|
+
if (this.cursorPos > 0)
|
|
178
|
+
this.cursorPos--;
|
|
179
|
+
this.renderScreen();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (k === 'right') {
|
|
183
|
+
if (this.cursorPos < this.inputBuffer.length)
|
|
184
|
+
this.cursorPos++;
|
|
185
|
+
this.renderScreen();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (k === 'tab') {
|
|
189
|
+
this.handleTab();
|
|
190
|
+
this.renderScreen();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (c && k === 'c') {
|
|
194
|
+
if (this.cancelRequested || this.status === 'thinking' || this.status === 'executing') {
|
|
195
|
+
if (this.onCancelRequest)
|
|
196
|
+
this.onCancelRequest();
|
|
165
197
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
198
|
+
else {
|
|
199
|
+
this.printLine('');
|
|
200
|
+
this.printLine(chalk.yellow('Use /exit to quit, or press Ctrl+C again to force quit'));
|
|
201
|
+
this.cancelRequested = true;
|
|
202
|
+
setTimeout(() => { this.cancelRequested = false; }, 2000);
|
|
169
203
|
}
|
|
170
|
-
|
|
171
|
-
|
|
204
|
+
this.renderScreen();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (c && k === 'l') {
|
|
208
|
+
this.clear();
|
|
209
|
+
this.renderScreen();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (c && k === 'x') {
|
|
213
|
+
await this.openExternalEditor();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (c && k === 'a') {
|
|
217
|
+
this.cursorPos = 0;
|
|
218
|
+
this.renderScreen();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (c && k === 'e') {
|
|
222
|
+
this.cursorPos = this.inputBuffer.length;
|
|
223
|
+
this.renderScreen();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (str && str.length === 1 && str.charCodeAt(0) >= 32) {
|
|
227
|
+
this.inputBuffer = this.inputBuffer.slice(0, this.cursorPos) + str + this.inputBuffer.slice(this.cursorPos);
|
|
228
|
+
this.cursorPos++;
|
|
229
|
+
if (str === '/') {
|
|
230
|
+
this.popupActive = true;
|
|
231
|
+
this.commandPopup.open();
|
|
232
|
+
this.commandPopup.setFilter('');
|
|
233
|
+
this.renderScreen();
|
|
172
234
|
return;
|
|
173
235
|
}
|
|
174
|
-
|
|
175
|
-
this.cursorPos += input.length;
|
|
176
|
-
if (input === '/') {
|
|
236
|
+
if (this.inputBuffer.startsWith('/') && this.inputBuffer.length > 1) {
|
|
177
237
|
this.popupActive = true;
|
|
178
238
|
this.commandPopup.open();
|
|
179
|
-
this.commandPopup.setFilter(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (this.inputBuffer.startsWith('/') && this.inputBuffer.length > 1) {
|
|
183
|
-
this.popupActive = true;
|
|
184
|
-
this.commandPopup.open();
|
|
185
|
-
this.commandPopup.setFilter(this.inputBuffer);
|
|
186
|
-
}
|
|
239
|
+
this.commandPopup.setFilter(this.inputBuffer.slice(1));
|
|
240
|
+
this.renderScreen();
|
|
241
|
+
return;
|
|
187
242
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
process.stdin.on('end', () => { });
|
|
243
|
+
}
|
|
244
|
+
this.renderScreen();
|
|
191
245
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.inputBuffer = line.trim();
|
|
197
|
-
if (this.inputBuffer && this.onInput) {
|
|
198
|
-
this.addHistory(this.inputBuffer);
|
|
199
|
-
this.onInput(this.inputBuffer);
|
|
246
|
+
clearPopupLines() {
|
|
247
|
+
if (this.popupLineCount > 0) {
|
|
248
|
+
try {
|
|
249
|
+
moveCursor(process.stdout, 0, -(this.popupLineCount + 1));
|
|
200
250
|
}
|
|
201
|
-
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
251
|
+
catch { }
|
|
252
|
+
for (let i = 0; i <= this.popupLineCount; i++) {
|
|
253
|
+
try {
|
|
254
|
+
cursorTo(process.stdout, 0);
|
|
255
|
+
clearLine(process.stdout, 1);
|
|
256
|
+
process.stdout.write('\n');
|
|
257
|
+
}
|
|
258
|
+
catch { }
|
|
259
|
+
}
|
|
260
|
+
this.popupLineCount = 0;
|
|
261
|
+
}
|
|
206
262
|
}
|
|
207
|
-
|
|
208
|
-
|
|
263
|
+
async handlePopupKeypress(str, key) {
|
|
264
|
+
const k = key.name || '';
|
|
265
|
+
if (k === 'escape') {
|
|
266
|
+
this.clearPopupLines();
|
|
209
267
|
this.popupActive = false;
|
|
210
268
|
this.commandPopup.close();
|
|
211
269
|
this.inputBuffer = '';
|
|
212
270
|
this.cursorPos = 0;
|
|
271
|
+
this.renderScreen();
|
|
213
272
|
return;
|
|
214
273
|
}
|
|
215
|
-
if (
|
|
274
|
+
if (k === 'up') {
|
|
216
275
|
this.commandPopup.moveUp();
|
|
276
|
+
this.renderScreen();
|
|
217
277
|
return;
|
|
218
278
|
}
|
|
219
|
-
if (
|
|
279
|
+
if (k === 'down') {
|
|
220
280
|
this.commandPopup.moveDown();
|
|
281
|
+
this.renderScreen();
|
|
221
282
|
return;
|
|
222
283
|
}
|
|
223
|
-
if (
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.
|
|
284
|
+
if (k === 'return' || k === 'enter') {
|
|
285
|
+
const cmd = this.commandPopup.getSelectedCommand();
|
|
286
|
+
this.popupActive = false;
|
|
287
|
+
this.commandPopup.close();
|
|
288
|
+
this.clearPopupLines();
|
|
289
|
+
if (cmd) {
|
|
290
|
+
const fullInput = `/${cmd} `;
|
|
291
|
+
if (this.onInput) {
|
|
292
|
+
await this.onInput(fullInput.trim());
|
|
293
|
+
}
|
|
231
294
|
}
|
|
295
|
+
this.renderScreen();
|
|
232
296
|
return;
|
|
233
297
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
298
|
+
if (k === 'backspace') {
|
|
299
|
+
this.commandPopup.deleteChar();
|
|
300
|
+
const filter = this.commandPopup.getFilterText();
|
|
301
|
+
if (filter.length === 0) {
|
|
302
|
+
this.clearPopupLines();
|
|
237
303
|
this.popupActive = false;
|
|
238
304
|
this.commandPopup.close();
|
|
239
|
-
|
|
240
|
-
this.
|
|
241
|
-
this.cursorPos = fullCmd.length;
|
|
305
|
+
this.inputBuffer = '/';
|
|
306
|
+
this.cursorPos = 1;
|
|
242
307
|
}
|
|
308
|
+
this.renderScreen();
|
|
243
309
|
return;
|
|
244
310
|
}
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.popupActive = false;
|
|
249
|
-
this.commandPopup.close();
|
|
250
|
-
this.inputBuffer = '';
|
|
251
|
-
this.cursorPos = 0;
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
this.commandPopup.deleteChar();
|
|
311
|
+
if (str && str.length === 1 && str.charCodeAt(0) >= 32) {
|
|
312
|
+
this.commandPopup.appendChar(str);
|
|
313
|
+
this.renderScreen();
|
|
255
314
|
return;
|
|
256
315
|
}
|
|
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
316
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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':
|
|
317
|
+
handleTab() {
|
|
318
|
+
const match = this.inputBuffer.match(/^\/?(\w*)$/);
|
|
319
|
+
if (match) {
|
|
320
|
+
const partial = match[1].toLowerCase();
|
|
321
|
+
const { ALL_COMMANDS } = require('./CommandPopup.js');
|
|
322
|
+
const cmds = ALL_COMMANDS.map((c) => c.command).filter((c) => c.startsWith(partial));
|
|
323
|
+
if (cmds.length === 1) {
|
|
324
|
+
this.inputBuffer = `/${cmds[0]} `;
|
|
336
325
|
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
326
|
}
|
|
382
327
|
}
|
|
383
328
|
}
|
|
384
|
-
getAllCommands() {
|
|
385
|
-
const { ALL_COMMANDS } = require('./CommandPopup.js');
|
|
386
|
-
return ALL_COMMANDS.map((c) => c.command);
|
|
387
|
-
}
|
|
388
329
|
async openExternalEditor() {
|
|
389
330
|
const editor = process.env.EDITOR || 'nano';
|
|
390
|
-
const { writeFileSync, unlinkSync, existsSync, mkdirSync } = await import('fs');
|
|
391
|
-
const { join } = await import('path');
|
|
331
|
+
const { writeFileSync, unlinkSync, existsSync, mkdirSync, readFileSync } = await import('fs');
|
|
392
332
|
const tmpDir = '/tmp/ys-agent';
|
|
393
333
|
if (!existsSync(tmpDir))
|
|
394
334
|
mkdirSync(tmpDir, { recursive: true });
|
|
395
|
-
const tmpFile =
|
|
335
|
+
const tmpFile = `/tmp/ys-agent/input-${Date.now()}.md`;
|
|
396
336
|
writeFileSync(tmpFile, this.inputBuffer, 'utf-8');
|
|
397
337
|
const { execSync } = await import('child_process');
|
|
398
338
|
try {
|
|
399
339
|
execSync(`${editor} "${tmpFile}"`, { stdio: 'inherit' });
|
|
400
|
-
const { readFileSync } = await import('fs');
|
|
401
340
|
const content = readFileSync(tmpFile, 'utf-8').trim();
|
|
402
341
|
if (content) {
|
|
403
342
|
this.inputBuffer = content;
|
|
404
343
|
this.cursorPos = content.length;
|
|
405
|
-
this.renderScreen();
|
|
406
344
|
}
|
|
407
345
|
}
|
|
408
346
|
catch { }
|
|
@@ -410,147 +348,42 @@ export class TUI {
|
|
|
410
348
|
unlinkSync(tmpFile);
|
|
411
349
|
}
|
|
412
350
|
catch { }
|
|
413
|
-
|
|
414
|
-
addHistory(input) {
|
|
415
|
-
if (this.inputHistory[this.inputHistory.length - 1] !== input) {
|
|
416
|
-
this.inputHistory.push(input);
|
|
417
|
-
}
|
|
418
|
-
this.historyIndex = -1;
|
|
419
|
-
this.saveHistory();
|
|
351
|
+
this.renderScreen();
|
|
420
352
|
}
|
|
421
353
|
renderScreen() {
|
|
422
354
|
if (!process.stdout.isTTY)
|
|
423
355
|
return;
|
|
424
|
-
const
|
|
425
|
-
|
|
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 { }
|
|
432
|
-
}
|
|
433
|
-
buildDisplayLines() {
|
|
434
|
-
const lines = [];
|
|
435
|
-
if (this.popupActive) {
|
|
436
|
-
const popupLines = this.commandPopup.render().split('\n');
|
|
437
|
-
lines.push(...popupLines);
|
|
438
|
-
}
|
|
439
|
-
lines.push(this.buildPromptLine());
|
|
440
|
-
return lines;
|
|
441
|
-
}
|
|
442
|
-
stop() {
|
|
443
|
-
this.running = false;
|
|
444
|
-
if (this.stdinRaw && process.stdin.isTTY) {
|
|
356
|
+
const promptLine = this.buildPromptLine();
|
|
357
|
+
if (this.popupLineCount > 0) {
|
|
445
358
|
try {
|
|
446
|
-
process.
|
|
359
|
+
moveCursor(process.stdout, 0, -(this.popupLineCount + 1));
|
|
447
360
|
}
|
|
448
361
|
catch { }
|
|
449
362
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
363
|
+
if (this.popupActive && this.commandPopup.isVisible()) {
|
|
364
|
+
const popup = this.commandPopup.render();
|
|
365
|
+
const popupLines = popup.split('\n');
|
|
366
|
+
this.popupLineCount = popupLines.length;
|
|
367
|
+
for (const l of popupLines) {
|
|
368
|
+
try {
|
|
369
|
+
cursorTo(process.stdout, 0);
|
|
370
|
+
clearLine(process.stdout, 1);
|
|
371
|
+
process.stdout.write(l + '\n');
|
|
372
|
+
}
|
|
373
|
+
catch { }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
this.popupLineCount = 0;
|
|
378
|
+
}
|
|
457
379
|
try {
|
|
458
|
-
const { cursorTo, clearLine } = require('readline');
|
|
459
380
|
cursorTo(process.stdout, 0);
|
|
460
381
|
clearLine(process.stdout, 1);
|
|
382
|
+
process.stdout.write(promptLine);
|
|
461
383
|
}
|
|
462
384
|
catch { }
|
|
463
385
|
}
|
|
464
|
-
|
|
465
|
-
const welcome = generateWelcome();
|
|
466
|
-
const lines = welcome.split('\n');
|
|
467
|
-
for (const line of lines) {
|
|
468
|
-
this.printLine(line);
|
|
469
|
-
}
|
|
470
|
-
this.printLine('');
|
|
471
|
-
if (configManager.getConfig().security.readOnlyMode) {
|
|
472
|
-
this.printWarning('Read-only mode active. File modifications will be blocked.');
|
|
473
|
-
}
|
|
474
|
-
if (this.approvalMode === 'yolo') {
|
|
475
|
-
this.printWarning('YOLO mode active. Agent will execute actions without confirmation.');
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
printHelp() {
|
|
479
|
-
const { ALL_COMMANDS } = require('./CommandPopup.js');
|
|
480
|
-
const w = Math.max(Math.min(process.stdout.columns || 80, 72), 30);
|
|
481
|
-
const top = `╔${'═'.repeat(w)}╗`;
|
|
482
|
-
const bottom = `╚${'═'.repeat(w)}╝`;
|
|
483
|
-
const lines = [top];
|
|
484
|
-
const title = ` YS Code Agent — Commands `;
|
|
485
|
-
const titlePad = Math.max(0, Math.floor((w - title.length) / 2));
|
|
486
|
-
lines.push(`║${' '.repeat(titlePad)}${chalk.cyan(title)}${' '.repeat(Math.max(0, w - titlePad - title.length))}║`);
|
|
487
|
-
lines.push(`╠${'═'.repeat(w)}╣`);
|
|
488
|
-
const categories = {};
|
|
489
|
-
for (const cmd of ALL_COMMANDS) {
|
|
490
|
-
if (!categories[cmd.category])
|
|
491
|
-
categories[cmd.category] = [];
|
|
492
|
-
categories[cmd.category].push(cmd);
|
|
493
|
-
}
|
|
494
|
-
let first = true;
|
|
495
|
-
for (const [cat, cmds] of Object.entries(categories)) {
|
|
496
|
-
if (!first)
|
|
497
|
-
lines.push(`║${' '.repeat(w)}║`);
|
|
498
|
-
first = false;
|
|
499
|
-
const icon = CATEGORY_ICONS[cat] || ' ';
|
|
500
|
-
lines.push(`║ ${icon} ${chalk.white(cat.toUpperCase())}${' '.repeat(Math.max(0, w - 5 - cat.length))}║`);
|
|
501
|
-
for (const cmd of cmds) {
|
|
502
|
-
const usage = cmd.usage || `/${cmd.command}`;
|
|
503
|
-
const cmdStr = chalk.yellow(usage);
|
|
504
|
-
const desc = chalk.gray(cmd.description);
|
|
505
|
-
const padding = ' '.repeat(Math.max(0, w - usage.length - cmd.description.length - 6));
|
|
506
|
-
lines.push(`║ ${cmdStr}${padding}${desc} ║`);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
lines.push(`╠${'═'.repeat(w)}╣`);
|
|
510
|
-
lines.push(`║ ${chalk.gray('Keyboard Shortcuts:')}${' '.repeat(Math.max(0, w - 22))}║`);
|
|
511
|
-
const shortcuts = [
|
|
512
|
-
['↑/↓', 'History navigation'],
|
|
513
|
-
['Tab', 'Autocomplete commands'],
|
|
514
|
-
['Ctrl+C', 'Cancel request'],
|
|
515
|
-
['Ctrl+L', 'Clear screen'],
|
|
516
|
-
['Ctrl+A', 'Line start'],
|
|
517
|
-
['Ctrl+E', 'Line end'],
|
|
518
|
-
['Ctrl+X', 'Open editor'],
|
|
519
|
-
['Esc', 'Cancel / clear input'],
|
|
520
|
-
];
|
|
521
|
-
for (const [key, desc] of shortcuts) {
|
|
522
|
-
const keyStr = chalk.cyan(key);
|
|
523
|
-
const descStr = chalk.gray(desc);
|
|
524
|
-
const padding = ' '.repeat(Math.max(0, w - key.length - desc.length - 6));
|
|
525
|
-
lines.push(`║ ${keyStr}${padding}${descStr} ║`);
|
|
526
|
-
}
|
|
527
|
-
lines.push(bottom);
|
|
528
|
-
for (const l of lines)
|
|
529
|
-
this.printLine(l);
|
|
530
|
-
}
|
|
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() {
|
|
386
|
+
buildPromptPrefix() {
|
|
554
387
|
const statusIndicator = STATUS_COLORS[this.status](INDICATORS[this.status]);
|
|
555
388
|
const config = configManager.getConfig();
|
|
556
389
|
const model = config.model.model;
|
|
@@ -567,28 +400,37 @@ export class TUI {
|
|
|
567
400
|
approvalBadge = chalk.cyan(' [safe]');
|
|
568
401
|
else if (this.approvalMode === 'yolo')
|
|
569
402
|
approvalBadge = chalk.red(' [yolo]');
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
403
|
+
return `${statusIndicator} ${chalk.cyan('ys')} ${chalk.yellow(`[${modelShort}]`)}${modeBadge}${approvalBadge} ${chalk.gray('›')} `;
|
|
404
|
+
}
|
|
405
|
+
buildPromptLine() {
|
|
406
|
+
const prefix = this.buildPromptPrefix();
|
|
407
|
+
const input = this.inputBuffer || '';
|
|
408
|
+
const cursor = chalk.gray('█');
|
|
409
|
+
let line;
|
|
410
|
+
if (this.cursorPos >= input.length) {
|
|
411
|
+
line = prefix + input + cursor;
|
|
578
412
|
}
|
|
579
413
|
else {
|
|
580
|
-
const before =
|
|
581
|
-
const after =
|
|
582
|
-
|
|
414
|
+
const before = input.slice(0, this.cursorPos);
|
|
415
|
+
const after = input.slice(this.cursorPos);
|
|
416
|
+
line = prefix + before + cursor + after;
|
|
417
|
+
}
|
|
418
|
+
return line;
|
|
419
|
+
}
|
|
420
|
+
showPrompt() {
|
|
421
|
+
if (this.running)
|
|
422
|
+
this.renderScreen();
|
|
423
|
+
}
|
|
424
|
+
addHistory(input) {
|
|
425
|
+
if (this.inputHistory[this.inputHistory.length - 1] !== input) {
|
|
426
|
+
this.inputHistory.push(input);
|
|
583
427
|
}
|
|
584
|
-
|
|
428
|
+
this.historyIndex = -1;
|
|
429
|
+
this.saveHistory();
|
|
585
430
|
}
|
|
586
431
|
setOnInput(handler) {
|
|
587
432
|
this.onInput = handler;
|
|
588
433
|
}
|
|
589
|
-
setOnSpecialKey(handler) {
|
|
590
|
-
this.onSpecialKey = handler;
|
|
591
|
-
}
|
|
592
434
|
setStatus(status) {
|
|
593
435
|
this.status = status;
|
|
594
436
|
}
|
|
@@ -610,32 +452,28 @@ export class TUI {
|
|
|
610
452
|
return;
|
|
611
453
|
}
|
|
612
454
|
try {
|
|
613
|
-
const { cursorTo, clearLine } = require('readline');
|
|
614
455
|
cursorTo(process.stdout, 0);
|
|
615
456
|
clearLine(process.stdout, 1);
|
|
616
|
-
|
|
457
|
+
process.stdout.write(line + '\n');
|
|
617
458
|
}
|
|
618
459
|
catch {
|
|
619
460
|
console.log(line);
|
|
620
461
|
}
|
|
621
|
-
this.outputLines.push(line);
|
|
622
|
-
if (this.outputLines.length > this.maxOutputLines) {
|
|
623
|
-
this.outputLines.shift();
|
|
624
|
-
}
|
|
625
462
|
}
|
|
626
463
|
printAssistant(message) {
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
|
|
464
|
+
const rendered = renderMarkdown(message);
|
|
465
|
+
for (const line of rendered.split('\n')) {
|
|
466
|
+
this.printLine(line);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
printWarning(message) {
|
|
470
|
+
this.printLine(chalk.yellow(`⚠ ${message}`));
|
|
630
471
|
}
|
|
631
472
|
printError(error) {
|
|
632
473
|
const w = Math.max(Math.min(process.stdout.columns || 80, 56), 10);
|
|
633
|
-
|
|
634
|
-
|
|
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));
|
|
474
|
+
this.printLine(chalk.red(`╔${'═'.repeat(w)}╗`));
|
|
475
|
+
this.printLine(chalk.red(`║`) + ` ${chalk.red('✗')} ${chalk.white(error)}${' '.repeat(Math.max(0, w - error.length - 7))}` + chalk.red(`║`));
|
|
476
|
+
this.printLine(chalk.red(`╚${'═'.repeat(w)}╝`));
|
|
639
477
|
}
|
|
640
478
|
printToolCall(toolName, args) {
|
|
641
479
|
const argsStr = Object.entries(args)
|
|
@@ -652,75 +490,91 @@ export class TUI {
|
|
|
652
490
|
this.printLine(chalk.gray(` ✕ ${chalk.red(result.error || 'failed')}`));
|
|
653
491
|
}
|
|
654
492
|
}
|
|
655
|
-
showStatusBar(
|
|
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}`));
|
|
668
|
-
}
|
|
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))}│`);
|
|
689
|
-
}
|
|
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('');
|
|
493
|
+
showStatusBar(_info) {
|
|
695
494
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
495
|
+
printWelcome() {
|
|
496
|
+
const welcome = generateWelcome();
|
|
497
|
+
for (const line of welcome.split('\n')) {
|
|
498
|
+
this.printLine(line);
|
|
499
|
+
}
|
|
500
|
+
this.printLine('');
|
|
501
|
+
if (configManager.getConfig().security.readOnlyMode) {
|
|
502
|
+
this.printWarning('Read-only mode active. File modifications will be blocked.');
|
|
503
|
+
}
|
|
504
|
+
if (this.approvalMode === 'yolo') {
|
|
505
|
+
this.printWarning('YOLO mode active. Agent will execute actions without confirmation.');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
printHelp() {
|
|
509
|
+
const { ALL_COMMANDS } = require('./CommandPopup.js');
|
|
510
|
+
const w = Math.max(Math.min(process.stdout.columns || 80, 72), 30);
|
|
511
|
+
const top = `╔${'═'.repeat(w)}╗`;
|
|
512
|
+
const bottom = `╚${'═'.repeat(w)}╝`;
|
|
513
|
+
const lines = [top];
|
|
514
|
+
const title = ` YS Code Agent — Commands `;
|
|
515
|
+
const titlePad = Math.max(0, Math.floor((w - title.length) / 2));
|
|
516
|
+
lines.push(`║${' '.repeat(titlePad)}${chalk.cyan(title)}${' '.repeat(Math.max(0, w - titlePad - title.length))}║`);
|
|
517
|
+
lines.push(`╠${'═'.repeat(w)}╣`);
|
|
518
|
+
const categories = {};
|
|
519
|
+
for (const cmd of ALL_COMMANDS) {
|
|
520
|
+
if (!categories[cmd.category])
|
|
521
|
+
categories[cmd.category] = [];
|
|
522
|
+
categories[cmd.category].push(cmd);
|
|
523
|
+
}
|
|
524
|
+
let first = true;
|
|
525
|
+
for (const [cat, cmds] of Object.entries(categories)) {
|
|
526
|
+
if (!first)
|
|
527
|
+
lines.push(`║${' '.repeat(w)}║`);
|
|
528
|
+
first = false;
|
|
529
|
+
const icon = CATEGORY_ICONS[cat] || ' ';
|
|
530
|
+
lines.push(`║ ${icon} ${chalk.white(cat.toUpperCase())}${' '.repeat(Math.max(0, w - 5 - cat.length))}║`);
|
|
531
|
+
for (const cmd of cmds) {
|
|
532
|
+
const usage = cmd.usage || `/${cmd.command}`;
|
|
533
|
+
const cmdStr = chalk.yellow(usage);
|
|
534
|
+
const desc = chalk.gray(cmd.description);
|
|
535
|
+
const padding = ' '.repeat(Math.max(0, w - usage.length - cmd.description.length - 6));
|
|
536
|
+
lines.push(`║ ${cmdStr}${padding}${desc} ║`);
|
|
700
537
|
}
|
|
701
|
-
catch { }
|
|
702
538
|
}
|
|
703
|
-
|
|
539
|
+
lines.push(`╠${'═'.repeat(w)}╣`);
|
|
540
|
+
lines.push(`║ ${chalk.gray('Keyboard Shortcuts:')}${' '.repeat(Math.max(0, w - 22))}║`);
|
|
541
|
+
const shortcuts = [
|
|
542
|
+
['↑/↓', 'History navigation'],
|
|
543
|
+
['Tab', 'Autocomplete commands'],
|
|
544
|
+
['Ctrl+C', 'Cancel request'],
|
|
545
|
+
['Ctrl+L', 'Clear screen'],
|
|
546
|
+
['Ctrl+A', 'Line start'],
|
|
547
|
+
['Ctrl+E', 'Line end'],
|
|
548
|
+
['Ctrl+X', 'Open editor'],
|
|
549
|
+
['Esc', 'Cancel / clear input'],
|
|
550
|
+
];
|
|
551
|
+
for (const [key, desc] of shortcuts) {
|
|
552
|
+
const keyStr = chalk.cyan(key);
|
|
553
|
+
const descStr = chalk.gray(desc);
|
|
554
|
+
const padding = ' '.repeat(Math.max(0, w - key.length - desc.length - 6));
|
|
555
|
+
lines.push(`║ ${keyStr}${padding}${descStr} ║`);
|
|
556
|
+
}
|
|
557
|
+
lines.push(bottom);
|
|
558
|
+
for (const l of lines)
|
|
559
|
+
this.printLine(l);
|
|
704
560
|
}
|
|
705
|
-
|
|
706
|
-
|
|
561
|
+
clear() {
|
|
562
|
+
console.clear();
|
|
707
563
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
cursorTo(stream, x, y);
|
|
564
|
+
stop() {
|
|
565
|
+
this.running = false;
|
|
566
|
+
process.stdin.removeAllListeners('keypress');
|
|
567
|
+
try {
|
|
568
|
+
process.stdin.setRawMode?.(false);
|
|
569
|
+
}
|
|
570
|
+
catch { }
|
|
571
|
+
}
|
|
572
|
+
destroy() {
|
|
573
|
+
this.stop();
|
|
574
|
+
}
|
|
575
|
+
getOutputLineCount() {
|
|
576
|
+
return 0;
|
|
722
577
|
}
|
|
723
|
-
catch { }
|
|
724
578
|
}
|
|
725
579
|
export const tui = new TUI();
|
|
726
580
|
//# sourceMappingURL=index.js.map
|