groundcrew-cli 0.15.1 → 0.15.3
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 +39 -33
- package/package.json +1 -1
- package/src/index.ts +58 -47
package/dist/index.js
CHANGED
|
@@ -415,49 +415,54 @@ function chatCompleter(line) {
|
|
|
415
415
|
return [[], line];
|
|
416
416
|
}
|
|
417
417
|
function setupInlineSuggestions(rl) {
|
|
418
|
-
let
|
|
418
|
+
let dropdownLines = 0;
|
|
419
|
+
let cursorCol = 0;
|
|
420
|
+
const getPromptLen = () => {
|
|
421
|
+
const p = rl._prompt || "";
|
|
422
|
+
return p.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
423
|
+
};
|
|
419
424
|
const clearGhost = () => {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
425
|
+
const buf = [];
|
|
426
|
+
if (dropdownLines > 0) {
|
|
427
|
+
for (let i = 0; i < dropdownLines; i++) {
|
|
428
|
+
buf.push("\x1B[B\x1B[2K");
|
|
423
429
|
}
|
|
424
|
-
|
|
425
|
-
|
|
430
|
+
buf.push(`\x1B[${dropdownLines}A`);
|
|
431
|
+
dropdownLines = 0;
|
|
432
|
+
}
|
|
433
|
+
if (cursorCol > 0) {
|
|
434
|
+
buf.push(`\x1B[${cursorCol}G\x1B[K`);
|
|
426
435
|
}
|
|
427
|
-
process.stdout.write("
|
|
436
|
+
if (buf.length) process.stdout.write(buf.join(""));
|
|
428
437
|
};
|
|
429
438
|
const showGhost = () => {
|
|
430
439
|
const line = rl.line;
|
|
431
|
-
if (!line || !line.startsWith("/") || line.includes(" "))
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
440
|
+
if (!line || !line.startsWith("/") || line.includes(" ")) return;
|
|
434
441
|
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(line));
|
|
435
442
|
if (matches.length === 0) return;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
for (let i = 1; i < shown.length; i++) {
|
|
454
|
-
const m = shown[i];
|
|
455
|
-
process.stdout.write(`
|
|
456
|
-
\x1B[2K \x1B[2m${m.cmd.padEnd(12)} \u2014 ${m.desc}\x1B[0m`);
|
|
443
|
+
const shown = matches.slice(0, 5);
|
|
444
|
+
const best = shown[0];
|
|
445
|
+
const remainder = best.cmd.slice(line.length);
|
|
446
|
+
if (!remainder && shown.length === 1) return;
|
|
447
|
+
cursorCol = getPromptLen() + line.length + 1;
|
|
448
|
+
const buf = [];
|
|
449
|
+
buf.push(`\x1B[${cursorCol}G\x1B[K`);
|
|
450
|
+
if (remainder) {
|
|
451
|
+
buf.push(`\x1B[2m${remainder}\x1B[0m`);
|
|
452
|
+
}
|
|
453
|
+
if (shown.length > 1 || shown.length === 1 && remainder) {
|
|
454
|
+
const count = shown.length;
|
|
455
|
+
for (let i = 0; i < count; i++) buf.push("\n");
|
|
456
|
+
buf.push(`\x1B[${count}A`);
|
|
457
|
+
for (let i = 0; i < count; i++) {
|
|
458
|
+
buf.push(`\x1B[B\r\x1B[2K`);
|
|
459
|
+
buf.push(` \x1B[36m${shown[i].cmd.padEnd(14)}\x1B[0m\x1B[2m${shown[i].desc}\x1B[0m`);
|
|
457
460
|
}
|
|
458
|
-
|
|
459
|
-
|
|
461
|
+
dropdownLines = count;
|
|
462
|
+
buf.push(`\x1B[${count}A`);
|
|
460
463
|
}
|
|
464
|
+
buf.push(`\x1B[${cursorCol}G`);
|
|
465
|
+
process.stdout.write(buf.join(""));
|
|
461
466
|
};
|
|
462
467
|
process.stdin.on("keypress", (_ch, key) => {
|
|
463
468
|
if (!key) return;
|
|
@@ -528,6 +533,7 @@ async function chat(explicitSession) {
|
|
|
528
533
|
const prompt = () => {
|
|
529
534
|
const isContinuation = continuationBuffer.length > 0;
|
|
530
535
|
const prefix = isContinuation ? `${dim(`[${current.id}]`)} ${dim("...")} ` : `${dim(`[${current.id}]`)} ${bold(">")} `;
|
|
536
|
+
rl.setPrompt(prefix);
|
|
531
537
|
rl.question(prefix, async (line) => {
|
|
532
538
|
if (line.endsWith("\\")) {
|
|
533
539
|
continuationBuffer.push(line.slice(0, -1));
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -540,71 +540,81 @@ function chatCompleter(line: string): [string[], string] {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
/**
|
|
543
|
-
* Show inline ghost
|
|
544
|
-
*
|
|
545
|
-
*
|
|
543
|
+
* Show inline ghost + multi-line dropdown as user types / commands.
|
|
544
|
+
* Best match completion shown inline (dimmed) after cursor.
|
|
545
|
+
* All matches (max 5) shown as dropdown lines below the prompt.
|
|
546
|
+
* Uses explicit column + relative cursor movement (no save/restore).
|
|
546
547
|
*/
|
|
547
548
|
function setupInlineSuggestions(rl: readline.Interface): void {
|
|
548
|
-
let
|
|
549
|
+
let dropdownLines = 0; // extra lines rendered below prompt
|
|
550
|
+
let cursorCol = 0; // 1-based column where typed text ends
|
|
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
556
|
|
|
550
557
|
const clearGhost = () => {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
558
|
+
const buf: string[] = [];
|
|
559
|
+
// Clear dropdown lines below prompt
|
|
560
|
+
if (dropdownLines > 0) {
|
|
561
|
+
for (let i = 0; i < dropdownLines; i++) {
|
|
562
|
+
buf.push("\x1b[B\x1b[2K"); // down + clear entire line
|
|
555
563
|
}
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
|
|
564
|
+
buf.push(`\x1b[${dropdownLines}A`); // back up to prompt line
|
|
565
|
+
dropdownLines = 0;
|
|
566
|
+
}
|
|
567
|
+
// Clear inline ghost on prompt line
|
|
568
|
+
if (cursorCol > 0) {
|
|
569
|
+
buf.push(`\x1b[${cursorCol}G\x1b[K`);
|
|
559
570
|
}
|
|
560
|
-
|
|
561
|
-
process.stdout.write("\x1b[u\x1b[K");
|
|
571
|
+
if (buf.length) process.stdout.write(buf.join(""));
|
|
562
572
|
};
|
|
563
573
|
|
|
564
574
|
const showGhost = () => {
|
|
565
575
|
const line = (rl as any).line as string;
|
|
566
|
-
if (!line || !line.startsWith("/") || line.includes(" "))
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
576
|
+
if (!line || !line.startsWith("/") || line.includes(" ")) return;
|
|
569
577
|
|
|
570
578
|
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(line));
|
|
571
579
|
if (matches.length === 0) return;
|
|
572
580
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const m = shown[i];
|
|
599
|
-
process.stdout.write(`\n\x1b[2K \x1b[2m${m.cmd.padEnd(12)} \u2014 ${m.desc}\x1b[0m`);
|
|
581
|
+
const shown = matches.slice(0, 5);
|
|
582
|
+
const best = shown[0];
|
|
583
|
+
const remainder = best.cmd.slice(line.length);
|
|
584
|
+
if (!remainder && shown.length === 1) return;
|
|
585
|
+
|
|
586
|
+
cursorCol = getPromptLen() + line.length + 1;
|
|
587
|
+
const buf: string[] = [];
|
|
588
|
+
|
|
589
|
+
// Inline ghost: show remainder of best match (dimmed) on prompt line
|
|
590
|
+
buf.push(`\x1b[${cursorCol}G\x1b[K`);
|
|
591
|
+
if (remainder) {
|
|
592
|
+
buf.push(`\x1b[2m${remainder}\x1b[0m`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Dropdown: show all matches below prompt
|
|
596
|
+
if (shown.length > 1 || (shown.length === 1 && remainder)) {
|
|
597
|
+
// Make room by writing newlines (handles terminal bottom scroll)
|
|
598
|
+
const count = shown.length;
|
|
599
|
+
for (let i = 0; i < count; i++) buf.push("\n");
|
|
600
|
+
buf.push(`\x1b[${count}A`); // back to prompt line
|
|
601
|
+
|
|
602
|
+
// Write each dropdown line: cyan command + dim description
|
|
603
|
+
for (let i = 0; i < count; i++) {
|
|
604
|
+
buf.push(`\x1b[B\r\x1b[2K`); // down + beginning of line + clear
|
|
605
|
+
buf.push(` \x1b[36m${shown[i].cmd.padEnd(14)}\x1b[0m\x1b[2m${shown[i].desc}\x1b[0m`);
|
|
600
606
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
607
|
+
dropdownLines = count;
|
|
608
|
+
|
|
609
|
+
// Back to prompt line
|
|
610
|
+
buf.push(`\x1b[${count}A`);
|
|
604
611
|
}
|
|
612
|
+
|
|
613
|
+
// Restore cursor to typing position on prompt line
|
|
614
|
+
buf.push(`\x1b[${cursorCol}G`);
|
|
615
|
+
process.stdout.write(buf.join(""));
|
|
605
616
|
};
|
|
606
617
|
|
|
607
|
-
// Listen to keypresses
|
|
608
618
|
process.stdin.on("keypress", (_ch: string, key: any) => {
|
|
609
619
|
if (!key) return;
|
|
610
620
|
clearGhost();
|
|
@@ -687,6 +697,7 @@ async function chat(explicitSession?: string): Promise<void> {
|
|
|
687
697
|
? `${dim(`[${current!.id}]`)} ${dim("...")} `
|
|
688
698
|
: `${dim(`[${current!.id}]`)} ${bold(">")} `;
|
|
689
699
|
|
|
700
|
+
rl.setPrompt(prefix);
|
|
690
701
|
rl.question(prefix, async (line) => {
|
|
691
702
|
// Line continuation with backslash
|
|
692
703
|
if (line.endsWith("\\")) {
|