groundcrew-cli 0.15.0 → 0.15.1
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 +34 -16
- package/package.json +1 -1
- package/src/index.ts +48 -24
package/dist/index.js
CHANGED
|
@@ -398,7 +398,7 @@ var CHAT_COMMANDS = [
|
|
|
398
398
|
{ cmd: "/history", desc: "Show completed tasks" },
|
|
399
399
|
{ cmd: "/queue", desc: "Show pending tasks" },
|
|
400
400
|
{ cmd: "/clear", desc: "Clear pending tasks" },
|
|
401
|
-
{ cmd: "/
|
|
401
|
+
{ cmd: "/exit", desc: "Exit chat" }
|
|
402
402
|
];
|
|
403
403
|
function chatCompleter(line) {
|
|
404
404
|
if (!line.startsWith("/")) return [[], line];
|
|
@@ -415,12 +415,16 @@ function chatCompleter(line) {
|
|
|
415
415
|
return [[], line];
|
|
416
416
|
}
|
|
417
417
|
function setupInlineSuggestions(rl) {
|
|
418
|
-
let
|
|
418
|
+
let ghostLines = 0;
|
|
419
419
|
const clearGhost = () => {
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
if (ghostLines > 0) {
|
|
421
|
+
for (let i = 0; i < ghostLines; i++) {
|
|
422
|
+
process.stdout.write("\x1B[B\x1B[2K");
|
|
423
|
+
}
|
|
424
|
+
process.stdout.write(`\x1B[${ghostLines}A`);
|
|
425
|
+
ghostLines = 0;
|
|
423
426
|
}
|
|
427
|
+
process.stdout.write("\x1B[u\x1B[K");
|
|
424
428
|
};
|
|
425
429
|
const showGhost = () => {
|
|
426
430
|
const line = rl.line;
|
|
@@ -429,22 +433,36 @@ function setupInlineSuggestions(rl) {
|
|
|
429
433
|
}
|
|
430
434
|
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(line));
|
|
431
435
|
if (matches.length === 0) return;
|
|
432
|
-
const best = matches[0];
|
|
433
|
-
const remainder = best.cmd.slice(line.length);
|
|
434
|
-
if (!remainder && matches.length === 1) return;
|
|
435
|
-
const ghost = `${remainder} \u2014 ${best.desc}`;
|
|
436
436
|
process.stdout.write("\x1B[s");
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
437
|
+
if (matches.length === 1) {
|
|
438
|
+
const best = matches[0];
|
|
439
|
+
const remainder = best.cmd.slice(line.length);
|
|
440
|
+
if (!remainder) return;
|
|
441
|
+
const ghost = `${remainder} \u2014 ${best.desc}`;
|
|
442
|
+
process.stdout.write("\x1B[K");
|
|
443
|
+
process.stdout.write(`\x1B[2m${ghost}\x1B[0m`);
|
|
444
|
+
process.stdout.write("\x1B[u");
|
|
445
|
+
} else {
|
|
446
|
+
const shown = matches.slice(0, 5);
|
|
447
|
+
process.stdout.write("\x1B[K");
|
|
448
|
+
const best = shown[0];
|
|
449
|
+
const remainder = best.cmd.slice(line.length);
|
|
450
|
+
if (remainder) {
|
|
451
|
+
process.stdout.write(`\x1B[2m${remainder} \u2014 ${best.desc}\x1B[0m`);
|
|
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`);
|
|
457
|
+
}
|
|
458
|
+
ghostLines = shown.length - 1;
|
|
459
|
+
process.stdout.write("\x1B[u");
|
|
460
|
+
}
|
|
441
461
|
};
|
|
442
462
|
process.stdin.on("keypress", (_ch, key) => {
|
|
443
463
|
if (!key) return;
|
|
444
464
|
clearGhost();
|
|
445
|
-
if (key.name !== "return" && key.name !== "tab"
|
|
446
|
-
setImmediate(showGhost);
|
|
447
|
-
} else if (key.name === "backspace") {
|
|
465
|
+
if (key.name !== "return" && key.name !== "tab") {
|
|
448
466
|
setImmediate(showGhost);
|
|
449
467
|
}
|
|
450
468
|
});
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -518,7 +518,7 @@ const CHAT_COMMANDS: Array<{ cmd: string; desc: string }> = [
|
|
|
518
518
|
{ cmd: "/history", desc: "Show completed tasks" },
|
|
519
519
|
{ cmd: "/queue", desc: "Show pending tasks" },
|
|
520
520
|
{ cmd: "/clear", desc: "Clear pending tasks" },
|
|
521
|
-
{ cmd: "/
|
|
521
|
+
{ cmd: "/exit", desc: "Exit chat" },
|
|
522
522
|
];
|
|
523
523
|
|
|
524
524
|
function chatCompleter(line: string): [string[], string] {
|
|
@@ -540,18 +540,25 @@ function chatCompleter(line: string): [string[], string] {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
/**
|
|
543
|
-
* Show inline ghost
|
|
544
|
-
*
|
|
543
|
+
* Show inline ghost suggestions as user types / commands.
|
|
544
|
+
* Single match: inline ghost after cursor.
|
|
545
|
+
* Multiple matches: multi-line dropdown below the prompt (max 5).
|
|
545
546
|
*/
|
|
546
547
|
function setupInlineSuggestions(rl: readline.Interface): void {
|
|
547
|
-
let
|
|
548
|
+
let ghostLines = 0; // how many extra lines we rendered below prompt
|
|
548
549
|
|
|
549
550
|
const clearGhost = () => {
|
|
550
|
-
if (
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
551
|
+
if (ghostLines > 0) {
|
|
552
|
+
// Move down and clear each ghost line, then move back up
|
|
553
|
+
for (let i = 0; i < ghostLines; i++) {
|
|
554
|
+
process.stdout.write("\x1b[B\x1b[2K"); // down + clear line
|
|
555
|
+
}
|
|
556
|
+
// Move back up to prompt line
|
|
557
|
+
process.stdout.write(`\x1b[${ghostLines}A`);
|
|
558
|
+
ghostLines = 0;
|
|
554
559
|
}
|
|
560
|
+
// Restore cursor and clear to end of line (for inline ghost)
|
|
561
|
+
process.stdout.write("\x1b[u\x1b[K");
|
|
555
562
|
};
|
|
556
563
|
|
|
557
564
|
const showGhost = () => {
|
|
@@ -563,28 +570,45 @@ function setupInlineSuggestions(rl: readline.Interface): void {
|
|
|
563
570
|
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(line));
|
|
564
571
|
if (matches.length === 0) return;
|
|
565
572
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
573
|
+
// Save cursor position
|
|
574
|
+
process.stdout.write("\x1b[s");
|
|
575
|
+
|
|
576
|
+
if (matches.length === 1) {
|
|
577
|
+
// Single match: inline ghost remainder + description
|
|
578
|
+
const best = matches[0];
|
|
579
|
+
const remainder = best.cmd.slice(line.length);
|
|
580
|
+
if (!remainder) return;
|
|
581
|
+
const ghost = `${remainder} \u2014 ${best.desc}`;
|
|
582
|
+
process.stdout.write("\x1b[K");
|
|
583
|
+
process.stdout.write(`\x1b[2m${ghost}\x1b[0m`);
|
|
584
|
+
process.stdout.write("\x1b[u");
|
|
585
|
+
} else {
|
|
586
|
+
// Multiple matches: show dropdown below prompt (max 5)
|
|
587
|
+
const shown = matches.slice(0, 5);
|
|
588
|
+
// Clear to end of line first
|
|
589
|
+
process.stdout.write("\x1b[K");
|
|
590
|
+
// Inline ghost for the best match remainder
|
|
591
|
+
const best = shown[0];
|
|
592
|
+
const remainder = best.cmd.slice(line.length);
|
|
593
|
+
if (remainder) {
|
|
594
|
+
process.stdout.write(`\x1b[2m${remainder} \u2014 ${best.desc}\x1b[0m`);
|
|
595
|
+
}
|
|
596
|
+
// Render dropdown lines below
|
|
597
|
+
for (let i = 1; i < shown.length; i++) {
|
|
598
|
+
const m = shown[i];
|
|
599
|
+
process.stdout.write(`\n\x1b[2K \x1b[2m${m.cmd.padEnd(12)} \u2014 ${m.desc}\x1b[0m`);
|
|
600
|
+
}
|
|
601
|
+
ghostLines = shown.length - 1;
|
|
602
|
+
// Restore cursor to typing position
|
|
603
|
+
process.stdout.write("\x1b[u");
|
|
604
|
+
}
|
|
578
605
|
};
|
|
579
606
|
|
|
580
607
|
// Listen to keypresses
|
|
581
608
|
process.stdin.on("keypress", (_ch: string, key: any) => {
|
|
582
609
|
if (!key) return;
|
|
583
|
-
// Clear ghost first, then show updated one on next tick
|
|
584
610
|
clearGhost();
|
|
585
|
-
if (key.name !== "return" && key.name !== "tab"
|
|
586
|
-
setImmediate(showGhost);
|
|
587
|
-
} else if (key.name === "backspace") {
|
|
611
|
+
if (key.name !== "return" && key.name !== "tab") {
|
|
588
612
|
setImmediate(showGhost);
|
|
589
613
|
}
|
|
590
614
|
});
|