groundcrew-cli 0.16.0 → 0.16.2
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 +31 -3
- package/package.json +1 -1
- package/src/index.ts +39 -6
package/dist/index.js
CHANGED
|
@@ -494,8 +494,20 @@ function readMultilineInput(sessionId, projectName, gitCtx) {
|
|
|
494
494
|
buf.push(" ".repeat(padWidth) + lines[i]);
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
|
+
let suggestionRows = 0;
|
|
498
|
+
if (lines.length === 1 && lines[0].startsWith("/") && !lines[0].includes(" ")) {
|
|
499
|
+
const partial = lines[0];
|
|
500
|
+
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(partial));
|
|
501
|
+
if (matches.length > 0 && partial.length >= 1) {
|
|
502
|
+
for (const m of matches) {
|
|
503
|
+
buf.push(`
|
|
504
|
+
${cyan(m.cmd.padEnd(14))} ${dim(m.desc)}`);
|
|
505
|
+
suggestionRows++;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
497
509
|
const lastRow = lines.length - 1;
|
|
498
|
-
const rowsUp = lastRow - crow;
|
|
510
|
+
const rowsUp = lastRow - crow + suggestionRows;
|
|
499
511
|
if (rowsUp > 0) buf.push(`\x1B[${rowsUp}A`);
|
|
500
512
|
buf.push("\r");
|
|
501
513
|
const col = padWidth + ccol;
|
|
@@ -638,12 +650,28 @@ function readMultilineInput(sessionId, projectName, gitCtx) {
|
|
|
638
650
|
continue;
|
|
639
651
|
}
|
|
640
652
|
if (str[i] === "\x1B" && i + 1 < str.length && str[i + 1] === "[") {
|
|
641
|
-
const csiMatch = str.slice(i).match(/^\x1b\[(\d+)
|
|
653
|
+
const csiMatch = str.slice(i).match(/^\x1b\[(\d+)(?:;(\d+))?u/);
|
|
642
654
|
if (csiMatch) {
|
|
643
655
|
const codepoint = parseInt(csiMatch[1], 10);
|
|
644
|
-
const modifier = parseInt(csiMatch[2], 10);
|
|
656
|
+
const modifier = csiMatch[2] ? parseInt(csiMatch[2], 10) : 1;
|
|
645
657
|
const isCtrl = modifier - 1 & 4;
|
|
646
658
|
const seqLen = csiMatch[0].length;
|
|
659
|
+
if (!isCtrl && modifier <= 1) {
|
|
660
|
+
switch (codepoint) {
|
|
661
|
+
case 9:
|
|
662
|
+
str = str.slice(0, i) + " " + str.slice(i + seqLen);
|
|
663
|
+
continue;
|
|
664
|
+
case 13:
|
|
665
|
+
str = str.slice(0, i) + "\r" + str.slice(i + seqLen);
|
|
666
|
+
continue;
|
|
667
|
+
case 27:
|
|
668
|
+
i += seqLen;
|
|
669
|
+
continue;
|
|
670
|
+
case 127:
|
|
671
|
+
str = str.slice(0, i) + "\x7F" + str.slice(i + seqLen);
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
647
675
|
if (isCtrl) {
|
|
648
676
|
switch (codepoint) {
|
|
649
677
|
case 99:
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -626,7 +626,7 @@ function readMultilineInput(sessionId: string, projectName: string, gitCtx: { br
|
|
|
626
626
|
const render = () => {
|
|
627
627
|
const buf: string[] = [];
|
|
628
628
|
|
|
629
|
-
// Move to start of input area (includes separator line)
|
|
629
|
+
// Move to start of input area (includes separator line + suggestions)
|
|
630
630
|
if (lastTermRow > 0) buf.push(`\x1b[${lastTermRow}A`);
|
|
631
631
|
buf.push("\r\x1b[J"); // col 0 + clear to end of screen
|
|
632
632
|
|
|
@@ -652,17 +652,31 @@ function readMultilineInput(sessionId: string, projectName: string, gitCtx: { br
|
|
|
652
652
|
}
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
// Auto-show slash command suggestions below input
|
|
656
|
+
let suggestionRows = 0;
|
|
657
|
+
if (lines.length === 1 && lines[0].startsWith("/") && !lines[0].includes(" ")) {
|
|
658
|
+
const partial = lines[0];
|
|
659
|
+
const matches = CHAT_COMMANDS.filter(c => c.cmd.startsWith(partial));
|
|
660
|
+
if (matches.length > 0 && partial.length >= 1) {
|
|
661
|
+
for (const m of matches) {
|
|
662
|
+
buf.push(`\n ${cyan(m.cmd.padEnd(14))} ${dim(m.desc)}`);
|
|
663
|
+
suggestionRows++;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
655
668
|
// Position cursor at (crow, ccol)
|
|
656
669
|
const lastRow = lines.length - 1;
|
|
657
|
-
|
|
670
|
+
// Position cursor at (crow, ccol) — move up past remaining input lines + suggestions
|
|
671
|
+
const rowsUp = (lastRow - crow) + suggestionRows;
|
|
658
672
|
if (rowsUp > 0) buf.push(`\x1b[${rowsUp}A`);
|
|
659
673
|
|
|
660
674
|
buf.push("\r");
|
|
661
675
|
const col = padWidth + ccol;
|
|
662
676
|
if (col > 0) buf.push(`\x1b[${col}C`);
|
|
663
677
|
|
|
664
|
-
// lastTermRow = rows
|
|
665
|
-
//
|
|
678
|
+
// lastTermRow = rows above cursor (separator + input lines above crow)
|
|
679
|
+
// Suggestion rows below cursor are cleared by \x1b[J on next render
|
|
666
680
|
lastTermRow = 1 + crow;
|
|
667
681
|
process.stdout.write(buf.join(""));
|
|
668
682
|
};
|
|
@@ -784,15 +798,34 @@ function readMultilineInput(sessionId: string, projectName: string, gitCtx: { br
|
|
|
784
798
|
if (str.startsWith("\x1b[F", i)) { ccol = lines[crow].length; render(); i += 3; continue; }
|
|
785
799
|
|
|
786
800
|
// CSI u (Kitty keyboard protocol) — decode \x1b[{codepoint};{modifier}u
|
|
801
|
+
// Also handles single-param \x1b[{codepoint}u (unmodified key)
|
|
787
802
|
// modifier bit 3 (value 4) = Ctrl, sent as bits+1 so modifier 5 = Ctrl
|
|
788
803
|
if (str[i] === "\x1b" && i + 1 < str.length && str[i + 1] === "[") {
|
|
789
|
-
const csiMatch = str.slice(i).match(/^\x1b\[(\d+)
|
|
804
|
+
const csiMatch = str.slice(i).match(/^\x1b\[(\d+)(?:;(\d+))?u/);
|
|
790
805
|
if (csiMatch) {
|
|
791
806
|
const codepoint = parseInt(csiMatch[1], 10);
|
|
792
|
-
const modifier = parseInt(csiMatch[2], 10);
|
|
807
|
+
const modifier = csiMatch[2] ? parseInt(csiMatch[2], 10) : 1;
|
|
793
808
|
const isCtrl = (modifier - 1) & 4;
|
|
794
809
|
const seqLen = csiMatch[0].length;
|
|
795
810
|
|
|
811
|
+
// Handle unmodified functional keys encoded as CSI u
|
|
812
|
+
if (!isCtrl && modifier <= 1) {
|
|
813
|
+
switch (codepoint) {
|
|
814
|
+
case 9: // Tab (unmodified)
|
|
815
|
+
// Delegate to Tab handler by injecting \t
|
|
816
|
+
str = str.slice(0, i) + "\t" + str.slice(i + seqLen);
|
|
817
|
+
continue;
|
|
818
|
+
case 13: // Enter (unmodified)
|
|
819
|
+
str = str.slice(0, i) + "\r" + str.slice(i + seqLen);
|
|
820
|
+
continue;
|
|
821
|
+
case 27: // Escape (unmodified)
|
|
822
|
+
i += seqLen; continue;
|
|
823
|
+
case 127: // Backspace (unmodified)
|
|
824
|
+
str = str.slice(0, i) + "\x7f" + str.slice(i + seqLen);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
796
829
|
if (isCtrl) {
|
|
797
830
|
switch (codepoint) {
|
|
798
831
|
case 99: // Ctrl+C
|