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 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+);(\d+)u/);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groundcrew-cli",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "CLI companion for Groundcrew — queue tasks, send feedback, monitor your Copilot agent from another terminal.",
5
5
  "type": "module",
6
6
  "bin": {
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
- const rowsUp = lastRow - crow;
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 from cursor back to top of separator
665
- // separator is 1 row, then crow rows of input below it
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+);(\d+)u/);
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