@rind-ai/cli 0.6.2 → 0.7.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/bin/rind.js +2 -2
- package/lib/assistant-renderer.js +42 -15
- package/lib/cli-input-actions.js +133 -19
- package/lib/cli-output-controller.js +82 -94
- package/lib/cli-runtime-controller.js +155 -8
- package/lib/cli-state.js +2 -2
- package/lib/command-controller.js +34 -0
- package/lib/components/assistant-message.js +54 -21
- package/lib/composer-terminal.js +1 -1
- package/lib/event-controller.js +9 -11
- package/lib/frontend-cli-implementation.js +108 -18
- package/lib/ipc.js +186 -0
- package/lib/line-editor.js +29 -4
- package/lib/local-slash-commands.js +2 -0
- package/lib/markdown-lines.js +142 -0
- package/lib/one-shot-progress.js +145 -145
- package/lib/one-shot.js +3 -3
- package/lib/prompt-history-store.js +54 -0
- package/lib/question-menu-state.js +10 -0
- package/lib/rendering.js +466 -102
- package/lib/runtime-client.js +12 -13
- package/lib/runtime-protocol.js +5 -0
- package/lib/send.js +53 -0
- package/lib/task-monitor-controller.js +45 -45
- package/lib/terminal-key.js +47 -3
- package/lib/text-width.js +2 -19
- package/lib/tool-display.js +18 -23
- package/lib/tui/cursor.js +17 -8
- package/lib/tui/input-buffer.js +69 -28
- package/lib/tui/tui.js +128 -30
- package/lib/turn-controller.js +13 -4
- package/package.json +5 -5
- package/lib/frontend-cli.js +0 -5
package/lib/tui/tui.js
CHANGED
|
@@ -5,9 +5,16 @@ export const CURSOR_MARKER = "\x1b_pi:c\x07";
|
|
|
5
5
|
|
|
6
6
|
const SYNC_START = "\x1b[?2026h";
|
|
7
7
|
const SYNC_END = "\x1b[?2026l";
|
|
8
|
+
const SHOW_CURSOR = "\x1b[?25h";
|
|
9
|
+
const HIDE_CURSOR = "\x1b[?25l";
|
|
8
10
|
const LINE_RESET = "\x1b[0m";
|
|
9
11
|
const PASTE_ENABLE = "\x1b[?2004h";
|
|
10
12
|
const PASTE_DISABLE = "\x1b[?2004l";
|
|
13
|
+
const KITTY_KEYBOARD_ENABLE = "\x1b[>7u\x1b[?u\x1b[c";
|
|
14
|
+
const KITTY_KEYBOARD_DISABLE = "\x1b[<u";
|
|
15
|
+
const MODIFY_OTHER_KEYS_ENABLE = "\x1b[>4;2m";
|
|
16
|
+
const MODIFY_OTHER_KEYS_DISABLE = "\x1b[>4;0m";
|
|
17
|
+
const KEYBOARD_QUERY_TIMEOUT_MS = 150;
|
|
11
18
|
const DEFAULT_COLUMNS = 80;
|
|
12
19
|
const DEFAULT_ROWS = 24;
|
|
13
20
|
const DEFAULT_RENDER_INTERVAL_MS = 16;
|
|
@@ -40,14 +47,20 @@ export function createTui(options = {}) {
|
|
|
40
47
|
let previousHeight = 0;
|
|
41
48
|
let cursorRow = 0;
|
|
42
49
|
let hardwareCursorRow = 0;
|
|
50
|
+
let hardwareCursorCol = 0;
|
|
43
51
|
let maxLinesRendered = 0;
|
|
44
52
|
let previousViewportTop = 0;
|
|
45
53
|
|
|
46
54
|
let clearOnShrink = false;
|
|
47
55
|
let cursorVisible = false;
|
|
48
56
|
let replayRequested = false;
|
|
57
|
+
let keyboardQueryActive = false;
|
|
58
|
+
let kittyKeyboardActive = false;
|
|
59
|
+
let modifyOtherKeysActive = false;
|
|
60
|
+
let keyboardQueryTimer = null;
|
|
61
|
+
let keyboardProtocolPushed = false;
|
|
49
62
|
const inputBuffer = createInputBuffer({
|
|
50
|
-
onSequence:
|
|
63
|
+
onSequence: handleInputSequence,
|
|
51
64
|
onPaste: (payload) => pasteHandler?.(payload),
|
|
52
65
|
setTimeout: schedule,
|
|
53
66
|
clearTimeout: cancelSchedule,
|
|
@@ -127,6 +140,7 @@ export function createTui(options = {}) {
|
|
|
127
140
|
output.on("resize", handleResize);
|
|
128
141
|
}
|
|
129
142
|
write(PASTE_ENABLE);
|
|
143
|
+
enableKeyboardProtocol();
|
|
130
144
|
hideCursor();
|
|
131
145
|
requestRender();
|
|
132
146
|
}
|
|
@@ -167,6 +181,7 @@ export function createTui(options = {}) {
|
|
|
167
181
|
input.setRawMode(rawModeBeforeStart);
|
|
168
182
|
}
|
|
169
183
|
write(PASTE_DISABLE);
|
|
184
|
+
disableKeyboardProtocol();
|
|
170
185
|
inputBuffer.clear();
|
|
171
186
|
}
|
|
172
187
|
|
|
@@ -174,6 +189,64 @@ export function createTui(options = {}) {
|
|
|
174
189
|
inputBuffer.feed(data);
|
|
175
190
|
}
|
|
176
191
|
|
|
192
|
+
function handleInputSequence(sequence) {
|
|
193
|
+
const kittyFlags = keyboardQueryActive ? kittyKeyboardFlags(sequence) : null;
|
|
194
|
+
if (kittyFlags !== null) {
|
|
195
|
+
clearKeyboardQuery();
|
|
196
|
+
if (kittyFlags > 0) {
|
|
197
|
+
kittyKeyboardActive = true;
|
|
198
|
+
} else {
|
|
199
|
+
enableModifyOtherKeys();
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (keyboardQueryActive && isDeviceAttributesResponse(sequence)) {
|
|
204
|
+
clearKeyboardQuery();
|
|
205
|
+
enableModifyOtherKeys();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
inputHandler?.(sequence);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function enableKeyboardProtocol() {
|
|
212
|
+
keyboardQueryActive = true;
|
|
213
|
+
keyboardProtocolPushed = true;
|
|
214
|
+
keyboardQueryTimer = schedule(() => {
|
|
215
|
+
keyboardQueryTimer = null;
|
|
216
|
+
keyboardQueryActive = false;
|
|
217
|
+
}, KEYBOARD_QUERY_TIMEOUT_MS);
|
|
218
|
+
write(KITTY_KEYBOARD_ENABLE);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function clearKeyboardQuery() {
|
|
222
|
+
keyboardQueryActive = false;
|
|
223
|
+
if (keyboardQueryTimer !== null) {
|
|
224
|
+
cancelSchedule(keyboardQueryTimer);
|
|
225
|
+
keyboardQueryTimer = null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function disableKeyboardProtocol() {
|
|
230
|
+
if (keyboardProtocolPushed) {
|
|
231
|
+
write(KITTY_KEYBOARD_DISABLE);
|
|
232
|
+
}
|
|
233
|
+
if (modifyOtherKeysActive) {
|
|
234
|
+
write(MODIFY_OTHER_KEYS_DISABLE);
|
|
235
|
+
}
|
|
236
|
+
clearKeyboardQuery();
|
|
237
|
+
keyboardProtocolPushed = false;
|
|
238
|
+
kittyKeyboardActive = false;
|
|
239
|
+
modifyOtherKeysActive = false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function enableModifyOtherKeys() {
|
|
243
|
+
if (kittyKeyboardActive || modifyOtherKeysActive) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
write(MODIFY_OTHER_KEYS_ENABLE);
|
|
247
|
+
modifyOtherKeysActive = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
177
250
|
function handleResize() {
|
|
178
251
|
requestRender();
|
|
179
252
|
}
|
|
@@ -260,6 +333,7 @@ export function createTui(options = {}) {
|
|
|
260
333
|
previousHeight = -1;
|
|
261
334
|
cursorRow = 0;
|
|
262
335
|
hardwareCursorRow = 0;
|
|
336
|
+
hardwareCursorCol = -1;
|
|
263
337
|
maxLinesRendered = 0;
|
|
264
338
|
previousViewportTop = 0;
|
|
265
339
|
}
|
|
@@ -292,14 +366,15 @@ export function createTui(options = {}) {
|
|
|
292
366
|
}
|
|
293
367
|
buffer += writableLine(newLines[index], width);
|
|
294
368
|
}
|
|
295
|
-
buffer += SYNC_END;
|
|
296
|
-
write(buffer);
|
|
297
369
|
cursorRow = Math.max(0, newLines.length - 1);
|
|
298
370
|
hardwareCursorRow = cursorRow;
|
|
371
|
+
hardwareCursorCol = -1;
|
|
372
|
+
buffer += hardwareCursorSequence(cursorPos, newLines);
|
|
373
|
+
buffer += SYNC_END;
|
|
374
|
+
write(buffer);
|
|
299
375
|
maxLinesRendered = clear ? newLines.length : Math.max(maxLinesRendered, newLines.length);
|
|
300
376
|
const bufferLength = Math.max(height, newLines.length);
|
|
301
377
|
previousViewportTop = Math.max(0, bufferLength - height);
|
|
302
|
-
positionHardwareCursor(cursorPos, newLines);
|
|
303
378
|
previousLines = newLines;
|
|
304
379
|
previousWidth = width;
|
|
305
380
|
previousHeight = height;
|
|
@@ -352,7 +427,7 @@ export function createTui(options = {}) {
|
|
|
352
427
|
const appendStart = appendedLines && firstChanged === previousLines.length && firstChanged > 0;
|
|
353
428
|
|
|
354
429
|
if (firstChanged === -1) {
|
|
355
|
-
|
|
430
|
+
emitCursorSequence(cursorPos, newLines);
|
|
356
431
|
previousViewportTop = prevViewportTop;
|
|
357
432
|
previousHeight = height;
|
|
358
433
|
return;
|
|
@@ -373,6 +448,9 @@ export function createTui(options = {}) {
|
|
|
373
448
|
return;
|
|
374
449
|
}
|
|
375
450
|
let buffer = SYNC_START;
|
|
451
|
+
cursorRow = targetRow;
|
|
452
|
+
hardwareCursorRow = targetRow;
|
|
453
|
+
hardwareCursorCol = -1;
|
|
376
454
|
const lineDiff = computeLineDiff(targetRow);
|
|
377
455
|
if (lineDiff > 0) {
|
|
378
456
|
buffer += `\x1b[${lineDiff}B`;
|
|
@@ -394,12 +472,11 @@ export function createTui(options = {}) {
|
|
|
394
472
|
if (moveBack > 0) {
|
|
395
473
|
buffer += `\x1b[${moveBack}A`;
|
|
396
474
|
}
|
|
475
|
+
buffer += hardwareCursorSequence(cursorPos, newLines);
|
|
397
476
|
buffer += SYNC_END;
|
|
398
477
|
write(buffer);
|
|
399
|
-
cursorRow = targetRow;
|
|
400
|
-
hardwareCursorRow = targetRow;
|
|
401
478
|
}
|
|
402
|
-
|
|
479
|
+
emitCursorSequence(cursorPos, newLines);
|
|
403
480
|
previousLines = newLines;
|
|
404
481
|
previousWidth = width;
|
|
405
482
|
previousHeight = height;
|
|
@@ -461,14 +538,15 @@ export function createTui(options = {}) {
|
|
|
461
538
|
buffer += `\x1b[${extraLines}A`;
|
|
462
539
|
}
|
|
463
540
|
|
|
464
|
-
buffer += SYNC_END;
|
|
465
|
-
write(buffer);
|
|
466
|
-
|
|
467
541
|
cursorRow = Math.max(0, newLines.length - 1);
|
|
468
542
|
hardwareCursorRow = finalCursorRow;
|
|
543
|
+
hardwareCursorCol = -1;
|
|
469
544
|
maxLinesRendered = Math.max(maxLinesRendered, newLines.length);
|
|
470
545
|
previousViewportTop = Math.max(prevViewportTop, finalCursorRow - height + 1);
|
|
471
|
-
|
|
546
|
+
buffer += hardwareCursorSequence(cursorPos, newLines);
|
|
547
|
+
buffer += SYNC_END;
|
|
548
|
+
write(buffer);
|
|
549
|
+
|
|
472
550
|
previousLines = newLines;
|
|
473
551
|
previousWidth = width;
|
|
474
552
|
previousHeight = height;
|
|
@@ -510,38 +588,49 @@ export function createTui(options = {}) {
|
|
|
510
588
|
return null;
|
|
511
589
|
}
|
|
512
590
|
|
|
513
|
-
function
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
hideCursor();
|
|
591
|
+
function hardwareCursorSequence(cursorPos, lines) {
|
|
592
|
+
if (!cursorPos || lines.length <= 0) {
|
|
593
|
+
if (!cursorVisible) {
|
|
594
|
+
return "";
|
|
518
595
|
}
|
|
519
|
-
|
|
596
|
+
cursorVisible = false;
|
|
597
|
+
return HIDE_CURSOR;
|
|
520
598
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
targetCol = Math.max(0, cursorPos.col);
|
|
599
|
+
const targetRow = Math.max(0, Math.min(cursorPos.row, lines.length - 1));
|
|
600
|
+
const targetCol = Math.max(0, cursorPos.col);
|
|
601
|
+
let sequence = "";
|
|
525
602
|
const rowDelta = targetRow - hardwareCursorRow;
|
|
526
|
-
let buffer = "";
|
|
527
603
|
if (rowDelta > 0) {
|
|
528
|
-
|
|
604
|
+
sequence += `\x1b[${rowDelta}B`;
|
|
529
605
|
} else if (rowDelta < 0) {
|
|
530
|
-
|
|
606
|
+
sequence += `\x1b[${-rowDelta}A`;
|
|
607
|
+
}
|
|
608
|
+
if (hardwareCursorCol !== targetCol) {
|
|
609
|
+
sequence += `\x1b[${targetCol + 1}G`;
|
|
610
|
+
}
|
|
611
|
+
if (!cursorVisible) {
|
|
612
|
+
sequence += SHOW_CURSOR;
|
|
531
613
|
}
|
|
532
|
-
buffer += `\x1b[${targetCol + 1}G`;
|
|
533
|
-
write(buffer);
|
|
534
614
|
hardwareCursorRow = targetRow;
|
|
535
|
-
|
|
615
|
+
hardwareCursorCol = targetCol;
|
|
616
|
+
cursorVisible = true;
|
|
617
|
+
return sequence;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function emitCursorSequence(cursorPos, lines) {
|
|
621
|
+
const sequence = hardwareCursorSequence(cursorPos, lines);
|
|
622
|
+
if (sequence) {
|
|
623
|
+
write(`${SYNC_START}${sequence}${SYNC_END}`);
|
|
624
|
+
}
|
|
536
625
|
}
|
|
537
626
|
|
|
538
627
|
function hideCursor() {
|
|
539
|
-
write(
|
|
628
|
+
write(HIDE_CURSOR);
|
|
540
629
|
cursorVisible = false;
|
|
541
630
|
}
|
|
542
631
|
|
|
543
632
|
function showCursor() {
|
|
544
|
-
write(
|
|
633
|
+
write(SHOW_CURSOR);
|
|
545
634
|
cursorVisible = true;
|
|
546
635
|
}
|
|
547
636
|
|
|
@@ -589,3 +678,12 @@ function finitePositive(value, fallback) {
|
|
|
589
678
|
const number = Number(value);
|
|
590
679
|
return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
|
|
591
680
|
}
|
|
681
|
+
|
|
682
|
+
function kittyKeyboardFlags(sequence) {
|
|
683
|
+
const match = String(sequence || "").match(/^\x1b\[\?(\d+)u$/);
|
|
684
|
+
return match ? Number(match[1]) : null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function isDeviceAttributesResponse(sequence) {
|
|
688
|
+
return /^\x1b\[\?[\d;]*c$/.test(sequence);
|
|
689
|
+
}
|
package/lib/turn-controller.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { runtimeMethods } from "./runtime-protocol.js";
|
|
2
2
|
|
|
3
|
-
export function createTurnController({
|
|
3
|
+
export function createTurnController({
|
|
4
|
+
request,
|
|
5
|
+
state,
|
|
6
|
+
output,
|
|
7
|
+
onTurnStart = () => {},
|
|
8
|
+
}) {
|
|
4
9
|
function submit(text, extra = {}) {
|
|
5
10
|
if (state.activeTurn) {
|
|
6
11
|
void submitQueuedInput(runtimeMethods.sessionSteer, text, text, "steering");
|
|
@@ -41,14 +46,18 @@ export function createTurnController({ request, state, output, refreshGoalState
|
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
async function run(text, extra = {}) {
|
|
49
|
+
let requestSucceeded = false;
|
|
44
50
|
try {
|
|
45
51
|
await request(runtimeMethods.sessionPrompt, { input: text, ...extra });
|
|
52
|
+
requestSucceeded = true;
|
|
46
53
|
} finally {
|
|
47
|
-
void refreshGoalState();
|
|
48
|
-
state.activeTurn = false;
|
|
49
|
-
state.interruptRequested = false;
|
|
50
54
|
output.closeAssistant();
|
|
51
55
|
output.refreshInputState();
|
|
56
|
+
if (!requestSucceeded) {
|
|
57
|
+
state.activeTurn = false;
|
|
58
|
+
state.interruptRequested = false;
|
|
59
|
+
output.refreshInputState();
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rind-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Rind command-line client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/Azzurroooo/rind.git",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"node": ">=18"
|
|
17
17
|
},
|
|
18
18
|
"optionalDependencies": {
|
|
19
|
-
"@rind-ai/runtime-win32-x64": "0.
|
|
20
|
-
"@rind-ai/runtime-linux-x64": "0.
|
|
21
|
-
"@rind-ai/runtime-darwin-x64": "0.
|
|
22
|
-
"@rind-ai/runtime-darwin-arm64": "0.
|
|
19
|
+
"@rind-ai/runtime-win32-x64": "0.7.0",
|
|
20
|
+
"@rind-ai/runtime-linux-x64": "0.7.0",
|
|
21
|
+
"@rind-ai/runtime-darwin-x64": "0.7.0",
|
|
22
|
+
"@rind-ai/runtime-darwin-arm64": "0.7.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public",
|