groundcrew-cli 0.15.3 → 0.15.5
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/index.js +19 -11
- package/package.json +1 -1
- package/src/index.ts +38 -20
package/dist/index.js
CHANGED
|
@@ -416,11 +416,7 @@ function chatCompleter(line) {
|
|
|
416
416
|
}
|
|
417
417
|
function setupInlineSuggestions(rl) {
|
|
418
418
|
let dropdownLines = 0;
|
|
419
|
-
let
|
|
420
|
-
const getPromptLen = () => {
|
|
421
|
-
const p = rl._prompt || "";
|
|
422
|
-
return p.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
423
|
-
};
|
|
419
|
+
let ghostLen = 0;
|
|
424
420
|
const clearGhost = () => {
|
|
425
421
|
const buf = [];
|
|
426
422
|
if (dropdownLines > 0) {
|
|
@@ -430,9 +426,8 @@ function setupInlineSuggestions(rl) {
|
|
|
430
426
|
buf.push(`\x1B[${dropdownLines}A`);
|
|
431
427
|
dropdownLines = 0;
|
|
432
428
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
429
|
+
buf.push("\x1B[K");
|
|
430
|
+
ghostLen = 0;
|
|
436
431
|
if (buf.length) process.stdout.write(buf.join(""));
|
|
437
432
|
};
|
|
438
433
|
const showGhost = () => {
|
|
@@ -444,11 +439,12 @@ function setupInlineSuggestions(rl) {
|
|
|
444
439
|
const best = shown[0];
|
|
445
440
|
const remainder = best.cmd.slice(line.length);
|
|
446
441
|
if (!remainder && shown.length === 1) return;
|
|
447
|
-
cursorCol = getPromptLen() + line.length + 1;
|
|
448
442
|
const buf = [];
|
|
449
|
-
buf.push(
|
|
443
|
+
buf.push("\x1B[K");
|
|
450
444
|
if (remainder) {
|
|
451
445
|
buf.push(`\x1B[2m${remainder}\x1B[0m`);
|
|
446
|
+
ghostLen = remainder.length;
|
|
447
|
+
buf.push(`\x1B[${remainder.length}D`);
|
|
452
448
|
}
|
|
453
449
|
if (shown.length > 1 || shown.length === 1 && remainder) {
|
|
454
450
|
const count = shown.length;
|
|
@@ -460,12 +456,24 @@ function setupInlineSuggestions(rl) {
|
|
|
460
456
|
}
|
|
461
457
|
dropdownLines = count;
|
|
462
458
|
buf.push(`\x1B[${count}A`);
|
|
459
|
+
buf.push(`\r`);
|
|
463
460
|
}
|
|
464
|
-
buf.push(`\x1B[${cursorCol}G`);
|
|
465
461
|
process.stdout.write(buf.join(""));
|
|
462
|
+
if (dropdownLines > 0) {
|
|
463
|
+
rl._refreshLine();
|
|
464
|
+
if (remainder) {
|
|
465
|
+
process.stdout.write(`\x1B[K\x1B[2m${remainder}\x1B[0m\x1B[${remainder.length}D`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
466
468
|
};
|
|
467
469
|
process.stdin.on("keypress", (_ch, key) => {
|
|
468
470
|
if (!key) return;
|
|
471
|
+
if (key.name === "return" && key.shift) {
|
|
472
|
+
const line = rl.line;
|
|
473
|
+
rl.line = line + "\\";
|
|
474
|
+
rl.cursor = rl.line.length;
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
469
477
|
clearGhost();
|
|
470
478
|
if (key.name !== "return" && key.name !== "tab") {
|
|
471
479
|
setImmediate(showGhost);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -543,16 +543,11 @@ function chatCompleter(line: string): [string[], string] {
|
|
|
543
543
|
* Show inline ghost + multi-line dropdown as user types / commands.
|
|
544
544
|
* Best match completion shown inline (dimmed) after cursor.
|
|
545
545
|
* All matches (max 5) shown as dropdown lines below the prompt.
|
|
546
|
-
* Uses
|
|
546
|
+
* Uses only relative cursor movement — no absolute column needed.
|
|
547
547
|
*/
|
|
548
548
|
function setupInlineSuggestions(rl: readline.Interface): void {
|
|
549
|
-
let dropdownLines = 0;
|
|
550
|
-
let
|
|
551
|
-
|
|
552
|
-
const getPromptLen = (): number => {
|
|
553
|
-
const p = (rl as any)._prompt as string || "";
|
|
554
|
-
return p.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
555
|
-
};
|
|
549
|
+
let dropdownLines = 0; // extra lines rendered below prompt
|
|
550
|
+
let ghostLen = 0; // length of inline ghost text on prompt line
|
|
556
551
|
|
|
557
552
|
const clearGhost = () => {
|
|
558
553
|
const buf: string[] = [];
|
|
@@ -564,10 +559,10 @@ function setupInlineSuggestions(rl: readline.Interface): void {
|
|
|
564
559
|
buf.push(`\x1b[${dropdownLines}A`); // back up to prompt line
|
|
565
560
|
dropdownLines = 0;
|
|
566
561
|
}
|
|
567
|
-
// Clear inline ghost
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
562
|
+
// Clear inline ghost: readline already repositioned cursor at end of
|
|
563
|
+
// typed text after processing the keypress, so just clear to EOL
|
|
564
|
+
buf.push("\x1b[K");
|
|
565
|
+
ghostLen = 0;
|
|
571
566
|
if (buf.length) process.stdout.write(buf.join(""));
|
|
572
567
|
};
|
|
573
568
|
|
|
@@ -583,40 +578,63 @@ function setupInlineSuggestions(rl: readline.Interface): void {
|
|
|
583
578
|
const remainder = best.cmd.slice(line.length);
|
|
584
579
|
if (!remainder && shown.length === 1) return;
|
|
585
580
|
|
|
586
|
-
cursorCol = getPromptLen() + line.length + 1;
|
|
587
581
|
const buf: string[] = [];
|
|
588
582
|
|
|
589
|
-
// Inline ghost:
|
|
590
|
-
|
|
583
|
+
// Inline ghost: cursor is already at end of typed text (readline did that)
|
|
584
|
+
// Just clear rest of line and write dimmed remainder
|
|
585
|
+
buf.push("\x1b[K");
|
|
591
586
|
if (remainder) {
|
|
592
587
|
buf.push(`\x1b[2m${remainder}\x1b[0m`);
|
|
588
|
+
ghostLen = remainder.length;
|
|
589
|
+
// Move cursor back to end of typed text
|
|
590
|
+
buf.push(`\x1b[${remainder.length}D`);
|
|
593
591
|
}
|
|
594
592
|
|
|
595
593
|
// Dropdown: show all matches below prompt
|
|
596
594
|
if (shown.length > 1 || (shown.length === 1 && remainder)) {
|
|
597
|
-
// Make room by writing newlines (handles terminal bottom scroll)
|
|
598
595
|
const count = shown.length;
|
|
596
|
+
// Make room by writing newlines (handles terminal bottom scroll)
|
|
599
597
|
for (let i = 0; i < count; i++) buf.push("\n");
|
|
600
598
|
buf.push(`\x1b[${count}A`); // back to prompt line
|
|
601
599
|
|
|
602
600
|
// Write each dropdown line: cyan command + dim description
|
|
603
601
|
for (let i = 0; i < count; i++) {
|
|
604
|
-
buf.push(`\x1b[B\r\x1b[2K`); // down +
|
|
602
|
+
buf.push(`\x1b[B\r\x1b[2K`); // down + col 1 + clear
|
|
605
603
|
buf.push(` \x1b[36m${shown[i].cmd.padEnd(14)}\x1b[0m\x1b[2m${shown[i].desc}\x1b[0m`);
|
|
606
604
|
}
|
|
607
605
|
dropdownLines = count;
|
|
608
606
|
|
|
609
|
-
// Back to prompt line
|
|
607
|
+
// Back to prompt line and restore horizontal position
|
|
608
|
+
// Move up to prompt line
|
|
610
609
|
buf.push(`\x1b[${count}A`);
|
|
610
|
+
// Move to column 1, then rewrite prompt+line to position cursor correctly
|
|
611
|
+
buf.push(`\r`);
|
|
612
|
+
// Let readline handle cursor positioning
|
|
611
613
|
}
|
|
612
614
|
|
|
613
|
-
// Restore cursor to typing position on prompt line
|
|
614
|
-
buf.push(`\x1b[${cursorCol}G`);
|
|
615
615
|
process.stdout.write(buf.join(""));
|
|
616
|
+
// After dropdown, force readline to redraw prompt line to fix cursor position
|
|
617
|
+
if (dropdownLines > 0) {
|
|
618
|
+
(rl as any)._refreshLine();
|
|
619
|
+
// Re-draw inline ghost since _refreshLine cleared it
|
|
620
|
+
if (remainder) {
|
|
621
|
+
process.stdout.write(`\x1b[K\x1b[2m${remainder}\x1b[0m\x1b[${remainder.length}D`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
616
624
|
};
|
|
617
625
|
|
|
618
626
|
process.stdin.on("keypress", (_ch: string, key: any) => {
|
|
619
627
|
if (!key) return;
|
|
628
|
+
|
|
629
|
+
// Shift+Enter: insert newline marker instead of submitting
|
|
630
|
+
if (key.name === "return" && key.shift) {
|
|
631
|
+
// Append backslash to trigger line continuation, then simulate Enter
|
|
632
|
+
const line = (rl as any).line as string;
|
|
633
|
+
(rl as any).line = line + "\\";
|
|
634
|
+
(rl as any).cursor = (rl as any).line.length;
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
620
638
|
clearGhost();
|
|
621
639
|
if (key.name !== "return" && key.name !== "tab") {
|
|
622
640
|
setImmediate(showGhost);
|