groundcrew-cli 0.15.15 → 0.15.16
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 +389 -280
- package/package.json +1 -1
- package/src/index.ts +358 -340
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import{createRequire}from'module';const require=createRequire(import.meta.url);
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import fs from "fs/promises";
|
|
5
5
|
import { existsSync } from "fs";
|
|
@@ -429,162 +429,331 @@ var CHAT_COMMANDS = [
|
|
|
429
429
|
{ cmd: "/clear", desc: "Clear pending tasks" },
|
|
430
430
|
{ cmd: "/exit", desc: "Exit chat" }
|
|
431
431
|
];
|
|
432
|
-
function
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
buf.push("\x1B[B\x1B[2K");
|
|
432
|
+
function readMultilineInput(sessionId) {
|
|
433
|
+
return new Promise((resolve) => {
|
|
434
|
+
const lines = [""];
|
|
435
|
+
let crow = 0;
|
|
436
|
+
let ccol = 0;
|
|
437
|
+
const padWidth = sessionId.length + 5;
|
|
438
|
+
let lastTermRow = 0;
|
|
439
|
+
let pasteBuffer = "";
|
|
440
|
+
let isPasting = false;
|
|
441
|
+
const fullText = () => lines.join("\n").trim();
|
|
442
|
+
const render = () => {
|
|
443
|
+
const buf = [];
|
|
444
|
+
if (lastTermRow > 0) buf.push(`\x1B[${lastTermRow}A`);
|
|
445
|
+
buf.push("\r\x1B[J");
|
|
446
|
+
for (let i = 0; i < lines.length; i++) {
|
|
447
|
+
if (i > 0) buf.push("\n");
|
|
448
|
+
if (i === 0) {
|
|
449
|
+
buf.push(dim(`[${sessionId}]`) + " " + bold(">") + " " + lines[i]);
|
|
450
|
+
} else {
|
|
451
|
+
buf.push(" ".repeat(padWidth) + lines[i]);
|
|
452
|
+
}
|
|
454
453
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
buf.push(
|
|
461
|
-
|
|
462
|
-
buf.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
454
|
+
const lastRow = lines.length - 1;
|
|
455
|
+
const rowsUp = lastRow - crow;
|
|
456
|
+
if (rowsUp > 0) buf.push(`\x1B[${rowsUp}A`);
|
|
457
|
+
buf.push("\r");
|
|
458
|
+
const col = padWidth + ccol;
|
|
459
|
+
if (col > 0) buf.push(`\x1B[${col}C`);
|
|
460
|
+
lastTermRow = crow;
|
|
461
|
+
process.stdout.write(buf.join(""));
|
|
462
|
+
};
|
|
463
|
+
const finish = (result) => {
|
|
464
|
+
process.stdin.removeListener("data", onData);
|
|
465
|
+
resolve(result);
|
|
466
|
+
};
|
|
467
|
+
const submit = () => {
|
|
468
|
+
const text = fullText();
|
|
469
|
+
const lastRow = lines.length - 1;
|
|
470
|
+
const rowsDown = lastRow - crow;
|
|
471
|
+
const buf = [];
|
|
472
|
+
if (rowsDown > 0) buf.push(`\x1B[${rowsDown}B`);
|
|
473
|
+
buf.push("\r");
|
|
474
|
+
const endCol = padWidth + lines[lastRow].length;
|
|
475
|
+
if (endCol > 0) buf.push(`\x1B[${endCol}C`);
|
|
476
|
+
buf.push("\n");
|
|
477
|
+
process.stdout.write(buf.join(""));
|
|
478
|
+
lastTermRow = 0;
|
|
479
|
+
finish(text || null);
|
|
480
|
+
};
|
|
481
|
+
const insertText = (text) => {
|
|
482
|
+
const chunks = text.split(/\r?\n/);
|
|
483
|
+
const before = lines[crow].slice(0, ccol);
|
|
484
|
+
const after = lines[crow].slice(ccol);
|
|
485
|
+
if (chunks.length === 1) {
|
|
486
|
+
lines[crow] = before + chunks[0] + after;
|
|
487
|
+
ccol += chunks[0].length;
|
|
488
|
+
} else {
|
|
489
|
+
lines[crow] = before + chunks[0];
|
|
490
|
+
const middle = chunks.slice(1, -1);
|
|
491
|
+
const last = chunks[chunks.length - 1];
|
|
492
|
+
lines.splice(crow + 1, 0, ...middle, last + after);
|
|
493
|
+
crow += chunks.length - 1;
|
|
494
|
+
ccol = last.length;
|
|
491
495
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
496
|
+
render();
|
|
497
|
+
};
|
|
498
|
+
const insertNewline = () => {
|
|
499
|
+
const before = lines[crow].slice(0, ccol);
|
|
500
|
+
const after = lines[crow].slice(ccol);
|
|
501
|
+
lines[crow] = before;
|
|
502
|
+
lines.splice(crow + 1, 0, after);
|
|
503
|
+
crow++;
|
|
504
|
+
ccol = 0;
|
|
505
|
+
render();
|
|
506
|
+
};
|
|
507
|
+
const doBackspace = () => {
|
|
508
|
+
if (ccol > 0) {
|
|
509
|
+
lines[crow] = lines[crow].slice(0, ccol - 1) + lines[crow].slice(ccol);
|
|
510
|
+
ccol--;
|
|
511
|
+
} else if (crow > 0) {
|
|
512
|
+
const prevLen = lines[crow - 1].length;
|
|
513
|
+
lines[crow - 1] += lines[crow];
|
|
514
|
+
lines.splice(crow, 1);
|
|
515
|
+
crow--;
|
|
516
|
+
ccol = prevLen;
|
|
501
517
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
518
|
+
render();
|
|
519
|
+
};
|
|
520
|
+
const doDelete = () => {
|
|
521
|
+
if (ccol < lines[crow].length) {
|
|
522
|
+
lines[crow] = lines[crow].slice(0, ccol) + lines[crow].slice(ccol + 1);
|
|
523
|
+
} else if (crow < lines.length - 1) {
|
|
524
|
+
lines[crow] += lines[crow + 1];
|
|
525
|
+
lines.splice(crow + 1, 1);
|
|
526
|
+
}
|
|
527
|
+
render();
|
|
528
|
+
};
|
|
529
|
+
const processKeys = (str) => {
|
|
530
|
+
let i = 0;
|
|
531
|
+
while (i < str.length) {
|
|
532
|
+
if (str.startsWith("\x1B[13;2u", i)) {
|
|
533
|
+
insertNewline();
|
|
534
|
+
i += 7;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (i + 1 < str.length && str[i] === "\x1B" && (str[i + 1] === "\r" || str[i + 1] === "\n")) {
|
|
538
|
+
insertNewline();
|
|
539
|
+
i += 2;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (str.startsWith("\x1B[A", i)) {
|
|
543
|
+
if (crow > 0) {
|
|
544
|
+
crow--;
|
|
545
|
+
ccol = Math.min(ccol, lines[crow].length);
|
|
546
|
+
render();
|
|
547
|
+
}
|
|
548
|
+
i += 3;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (str.startsWith("\x1B[B", i)) {
|
|
552
|
+
if (crow < lines.length - 1) {
|
|
553
|
+
crow++;
|
|
554
|
+
ccol = Math.min(ccol, lines[crow].length);
|
|
555
|
+
render();
|
|
556
|
+
}
|
|
557
|
+
i += 3;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (str.startsWith("\x1B[C", i)) {
|
|
561
|
+
if (ccol < lines[crow].length) ccol++;
|
|
562
|
+
else if (crow < lines.length - 1) {
|
|
563
|
+
crow++;
|
|
564
|
+
ccol = 0;
|
|
565
|
+
}
|
|
566
|
+
render();
|
|
567
|
+
i += 3;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (str.startsWith("\x1B[D", i)) {
|
|
571
|
+
if (ccol > 0) ccol--;
|
|
572
|
+
else if (crow > 0) {
|
|
573
|
+
crow--;
|
|
574
|
+
ccol = lines[crow].length;
|
|
575
|
+
}
|
|
576
|
+
render();
|
|
577
|
+
i += 3;
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (str.startsWith("\x1B[3~", i)) {
|
|
581
|
+
doDelete();
|
|
582
|
+
i += 4;
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
if (str.startsWith("\x1B[H", i)) {
|
|
586
|
+
ccol = 0;
|
|
587
|
+
render();
|
|
588
|
+
i += 3;
|
|
589
|
+
continue;
|
|
526
590
|
}
|
|
527
|
-
|
|
591
|
+
if (str.startsWith("\x1B[F", i)) {
|
|
592
|
+
ccol = lines[crow].length;
|
|
593
|
+
render();
|
|
594
|
+
i += 3;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (str[i] === "\x1B" && i + 1 < str.length && str[i + 1] === "[") {
|
|
598
|
+
let j = i + 2;
|
|
599
|
+
while (j < str.length && str.charCodeAt(j) >= 48 && str.charCodeAt(j) <= 63) j++;
|
|
600
|
+
if (j < str.length) j++;
|
|
601
|
+
i = j;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if (str[i] === "\x1B") {
|
|
605
|
+
i++;
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (str[i] === "") {
|
|
609
|
+
if (fullText()) {
|
|
610
|
+
const lastRow = lines.length - 1;
|
|
611
|
+
const rowsDown = lastRow - crow;
|
|
612
|
+
if (rowsDown > 0) process.stdout.write(`\x1B[${rowsDown}B`);
|
|
613
|
+
process.stdout.write("\n");
|
|
614
|
+
lines.length = 0;
|
|
615
|
+
lines.push("");
|
|
616
|
+
crow = 0;
|
|
617
|
+
ccol = 0;
|
|
618
|
+
lastTermRow = 0;
|
|
619
|
+
render();
|
|
620
|
+
} else {
|
|
621
|
+
process.stdout.write("\n");
|
|
622
|
+
finish(null);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
i++;
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (str[i] === "") {
|
|
629
|
+
if (fullText()) {
|
|
630
|
+
doDelete();
|
|
631
|
+
} else {
|
|
632
|
+
process.stdout.write("\n");
|
|
633
|
+
finish(null);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
i++;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (str[i] === "") {
|
|
640
|
+
ccol = 0;
|
|
641
|
+
render();
|
|
642
|
+
i++;
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (str[i] === "") {
|
|
646
|
+
ccol = lines[crow].length;
|
|
647
|
+
render();
|
|
648
|
+
i++;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (str[i] === "") {
|
|
652
|
+
lines[crow] = lines[crow].slice(ccol);
|
|
653
|
+
ccol = 0;
|
|
654
|
+
render();
|
|
655
|
+
i++;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (str[i] === "\v") {
|
|
659
|
+
lines[crow] = lines[crow].slice(0, ccol);
|
|
660
|
+
render();
|
|
661
|
+
i++;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
if (str[i] === "") {
|
|
665
|
+
const before = lines[crow].slice(0, ccol);
|
|
666
|
+
const stripped = before.replace(/\s+$/, "");
|
|
667
|
+
const sp = stripped.lastIndexOf(" ");
|
|
668
|
+
const newBefore = sp >= 0 ? stripped.slice(0, sp + 1) : "";
|
|
669
|
+
lines[crow] = newBefore + lines[crow].slice(ccol);
|
|
670
|
+
ccol = newBefore.length;
|
|
671
|
+
render();
|
|
672
|
+
i++;
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (str[i] === "\n") {
|
|
676
|
+
insertNewline();
|
|
677
|
+
i++;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (str[i] === "\r") {
|
|
681
|
+
submit();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (str[i] === "\x7F" || str[i] === "\b") {
|
|
685
|
+
doBackspace();
|
|
686
|
+
i++;
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (str[i] === " ") {
|
|
690
|
+
if (lines.length === 1 && lines[0].startsWith("/")) {
|
|
691
|
+
const matches = CHAT_COMMANDS.filter((c) => c.cmd.startsWith(lines[0]));
|
|
692
|
+
if (matches.length === 1) {
|
|
693
|
+
lines[0] = matches[0].cmd + " ";
|
|
694
|
+
ccol = lines[0].length;
|
|
695
|
+
render();
|
|
696
|
+
} else if (matches.length > 1) {
|
|
697
|
+
const lastRow = lines.length - 1;
|
|
698
|
+
const rowsDown = lastRow - crow;
|
|
699
|
+
if (rowsDown > 0) process.stdout.write(`\x1B[${rowsDown}B`);
|
|
700
|
+
process.stdout.write("\n");
|
|
701
|
+
for (const m of matches) {
|
|
702
|
+
process.stdout.write(` ${cyan(m.cmd.padEnd(14))} ${dim(m.desc)}
|
|
703
|
+
`);
|
|
704
|
+
}
|
|
705
|
+
lastTermRow = 0;
|
|
706
|
+
render();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
i++;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const code = str.charCodeAt(i);
|
|
713
|
+
if (code >= 32) {
|
|
714
|
+
lines[crow] = lines[crow].slice(0, ccol) + str[i] + lines[crow].slice(ccol);
|
|
715
|
+
ccol++;
|
|
716
|
+
render();
|
|
717
|
+
}
|
|
718
|
+
i++;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
const onData = (data) => {
|
|
722
|
+
let str = data.toString();
|
|
723
|
+
const ps = str.indexOf("\x1B[200~");
|
|
724
|
+
if (ps !== -1) {
|
|
725
|
+
isPasting = true;
|
|
726
|
+
const before = str.slice(0, ps);
|
|
727
|
+
if (before) processKeys(before);
|
|
728
|
+
str = str.slice(ps + 6);
|
|
528
729
|
}
|
|
529
730
|
if (isPasting) {
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
pasteBuffer += str.slice(0,
|
|
533
|
-
const afterPaste = str.slice(pasteEnd + 6);
|
|
731
|
+
const pe = str.indexOf("\x1B[201~");
|
|
732
|
+
if (pe !== -1) {
|
|
733
|
+
pasteBuffer += str.slice(0, pe);
|
|
534
734
|
isPasting = false;
|
|
535
735
|
const pasted = pasteBuffer.replace(/[\r\n]+$/, "");
|
|
536
736
|
pasteBuffer = "";
|
|
537
|
-
if (pasted
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
originalStdinEmit(event, Buffer.from(lines[i] + "\\\r"));
|
|
541
|
-
}
|
|
542
|
-
originalStdinEmit(event, Buffer.from(lines[lines.length - 1]));
|
|
543
|
-
} else {
|
|
544
|
-
originalStdinEmit(event, Buffer.from(pasted));
|
|
545
|
-
}
|
|
546
|
-
if (afterPaste) {
|
|
547
|
-
return originalStdinEmit(event, Buffer.from(afterPaste));
|
|
548
|
-
}
|
|
549
|
-
return false;
|
|
737
|
+
if (pasted) insertText(pasted);
|
|
738
|
+
const after = str.slice(pe + 6);
|
|
739
|
+
if (after) processKeys(after);
|
|
550
740
|
} else {
|
|
551
741
|
pasteBuffer += str;
|
|
552
|
-
return false;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
if (str.includes("\x1B[13;2u")) {
|
|
556
|
-
const replaced = str.replace(/\x1b\[13;2u/g, "\\\r");
|
|
557
|
-
return originalStdinEmit(event, Buffer.from(replaced));
|
|
558
|
-
}
|
|
559
|
-
if (str === "\x1B\r" || str === "\x1B\n") {
|
|
560
|
-
return originalStdinEmit(event, Buffer.from("\\\r"));
|
|
561
|
-
}
|
|
562
|
-
if (str === "\n") {
|
|
563
|
-
return originalStdinEmit(event, Buffer.from("\\\r"));
|
|
564
|
-
}
|
|
565
|
-
if ((str === "\x7F" || str === "\b") && continuationBuffer.length > 0) {
|
|
566
|
-
const currentLine = rl.line;
|
|
567
|
-
const cursor = rl.cursor;
|
|
568
|
-
if (cursor === 0 && currentLine.length === 0) {
|
|
569
|
-
const prevLine = continuationBuffer.pop();
|
|
570
|
-
rl.line = prevLine;
|
|
571
|
-
rl.cursor = prevLine.length;
|
|
572
|
-
const isCont = continuationBuffer.length > 0;
|
|
573
|
-
const prefix = isCont ? `${dim(`[${current.id}]`)} ${dim("...")} ` : `${dim(`[${current.id}]`)} ${bold(">")} `;
|
|
574
|
-
rl.setPrompt(prefix);
|
|
575
|
-
rl._refreshLine();
|
|
576
|
-
return false;
|
|
577
742
|
}
|
|
743
|
+
return;
|
|
578
744
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
745
|
+
processKeys(str);
|
|
746
|
+
};
|
|
747
|
+
process.stdin.on("data", onData);
|
|
748
|
+
render();
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
async function chat(explicitSession) {
|
|
752
|
+
process.stdout.write("\x1B[?2004h\x1B[>1u");
|
|
582
753
|
const rl = readline.createInterface({
|
|
583
754
|
input: process.stdin,
|
|
584
|
-
output: process.stdout
|
|
585
|
-
completer: chatCompleter
|
|
755
|
+
output: process.stdout
|
|
586
756
|
});
|
|
587
|
-
setupInlineSuggestions(rl);
|
|
588
757
|
let current = null;
|
|
589
758
|
if (explicitSession) {
|
|
590
759
|
const dir = path.join(SESSIONS_DIR, explicitSession);
|
|
@@ -601,6 +770,7 @@ async function chat(explicitSession) {
|
|
|
601
770
|
return;
|
|
602
771
|
}
|
|
603
772
|
}
|
|
773
|
+
rl.close();
|
|
604
774
|
const projectName = path.basename(current.cwd);
|
|
605
775
|
const banner = [
|
|
606
776
|
" \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 ",
|
|
@@ -633,152 +803,91 @@ async function chat(explicitSession) {
|
|
|
633
803
|
console.log(dim(" \u2502") + " ".repeat(W) + dim("\u2502"));
|
|
634
804
|
console.log(dim(" \u2570" + "\u2500".repeat(W) + "\u256F"));
|
|
635
805
|
console.log();
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (line || continuationBuffer.length > 0) {
|
|
640
|
-
continuationBuffer = [];
|
|
641
|
-
process.stdout.write("\n");
|
|
642
|
-
prompt();
|
|
643
|
-
} else {
|
|
644
|
-
process.stdout.write("\x1B[?2004l\x1B[<u");
|
|
645
|
-
console.log(dim("\nBye."));
|
|
646
|
-
process.exit(0);
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
rl.on("close", () => {
|
|
806
|
+
process.stdin.setRawMode(true);
|
|
807
|
+
process.stdin.resume();
|
|
808
|
+
const exitChat = () => {
|
|
650
809
|
process.stdout.write("\x1B[?2004l\x1B[<u");
|
|
651
|
-
|
|
810
|
+
process.stdin.setRawMode(false);
|
|
811
|
+
console.log(dim("Bye."));
|
|
652
812
|
process.exit(0);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
await add(fullText, 0, current.dir);
|
|
672
|
-
} else {
|
|
673
|
-
await add(fullText, 0, current.dir);
|
|
674
|
-
}
|
|
675
|
-
} catch (err) {
|
|
676
|
-
console.error(red(err.message));
|
|
677
|
-
}
|
|
813
|
+
};
|
|
814
|
+
while (true) {
|
|
815
|
+
const text = await readMultilineInput(current.id);
|
|
816
|
+
if (text === null) exitChat();
|
|
817
|
+
if (!text) continue;
|
|
818
|
+
const trimmed = text.trim();
|
|
819
|
+
try {
|
|
820
|
+
if (trimmed === "/quit" || trimmed === "/exit") exitChat();
|
|
821
|
+
if (trimmed === "/sessions") {
|
|
822
|
+
const choices = await listSessionChoices();
|
|
823
|
+
if (choices.length === 0) {
|
|
824
|
+
console.log(dim("No active sessions."));
|
|
825
|
+
} else {
|
|
826
|
+
choices.forEach((s, i) => {
|
|
827
|
+
const marker = s.id === current.id ? green("*") : " ";
|
|
828
|
+
const pName = path.basename(s.cwd);
|
|
829
|
+
console.log(` ${marker} ${bold(String(i + 1))}. ${cyan(s.id)} ${dim(pName)} | ${s.status} | ${s.minutes}min | ${s.tasks} done`);
|
|
830
|
+
});
|
|
678
831
|
}
|
|
679
|
-
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
const trimmed = line.trim();
|
|
683
|
-
if (!trimmed) {
|
|
684
|
-
prompt();
|
|
685
|
-
return;
|
|
832
|
+
continue;
|
|
686
833
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (trimmed === "/sessions") {
|
|
694
|
-
const choices = await listSessionChoices();
|
|
695
|
-
if (choices.length === 0) {
|
|
696
|
-
console.log(dim("No active sessions."));
|
|
697
|
-
} else {
|
|
698
|
-
choices.forEach((s, i) => {
|
|
699
|
-
const marker = s.id === current.id ? green("*") : " ";
|
|
700
|
-
const pName = path.basename(s.cwd);
|
|
701
|
-
console.log(` ${marker} ${bold(String(i + 1))}. ${cyan(s.id)} ${dim(pName)} | ${s.status} | ${s.minutes}min | ${s.tasks} done`);
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
prompt();
|
|
705
|
-
return;
|
|
834
|
+
if (trimmed.startsWith("/switch")) {
|
|
835
|
+
const arg = trimmed.slice(7).trim();
|
|
836
|
+
const choices = await listSessionChoices();
|
|
837
|
+
if (choices.length === 0) {
|
|
838
|
+
console.log(red("No active sessions."));
|
|
839
|
+
continue;
|
|
706
840
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
if (idx >= 0 && idx < choices.length) {
|
|
717
|
-
current = choices[idx];
|
|
718
|
-
console.log(green(`Switched to ${current.id} (${path.basename(current.cwd)})`));
|
|
719
|
-
} else {
|
|
720
|
-
choices.forEach((s, i) => {
|
|
721
|
-
const marker = s.id === current.id ? green("*") : " ";
|
|
722
|
-
console.log(` ${marker} ${bold(String(i + 1))}. ${cyan(s.id)} ${dim(path.basename(s.cwd))}`);
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
prompt();
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
if (trimmed === "/status") {
|
|
729
|
-
await status(current.dir);
|
|
730
|
-
prompt();
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
if (trimmed === "/history") {
|
|
734
|
-
await history();
|
|
735
|
-
prompt();
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
if (trimmed.startsWith("/feedback ")) {
|
|
739
|
-
const msg = trimmed.slice(10).trim();
|
|
740
|
-
if (msg) {
|
|
741
|
-
await feedback(msg, current.dir);
|
|
742
|
-
} else {
|
|
743
|
-
console.log(red("Usage: /feedback <message>"));
|
|
744
|
-
}
|
|
745
|
-
prompt();
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
if (trimmed.startsWith("/priority ")) {
|
|
749
|
-
const task = trimmed.slice(10).trim();
|
|
750
|
-
if (task) {
|
|
751
|
-
await add(task, 9, current.dir);
|
|
752
|
-
} else {
|
|
753
|
-
console.log(red("Usage: /priority <task>"));
|
|
754
|
-
}
|
|
755
|
-
prompt();
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
if (trimmed === "/queue") {
|
|
759
|
-
await listQueueCmd(current.dir);
|
|
760
|
-
prompt();
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
if (trimmed === "/clear") {
|
|
764
|
-
await clear(current.dir);
|
|
765
|
-
prompt();
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
if (trimmed.startsWith("/")) {
|
|
769
|
-
console.log(red(`Unknown command: ${trimmed.split(" ")[0]}`));
|
|
770
|
-
console.log(dim(" Press Tab to see available commands"));
|
|
771
|
-
prompt();
|
|
772
|
-
return;
|
|
841
|
+
const idx = parseInt(arg) - 1;
|
|
842
|
+
if (idx >= 0 && idx < choices.length) {
|
|
843
|
+
current = choices[idx];
|
|
844
|
+
console.log(green(`Switched to ${current.id} (${path.basename(current.cwd)})`));
|
|
845
|
+
} else {
|
|
846
|
+
choices.forEach((s, i) => {
|
|
847
|
+
const marker = s.id === current.id ? green("*") : " ";
|
|
848
|
+
console.log(` ${marker} ${bold(String(i + 1))}. ${cyan(s.id)} ${dim(path.basename(s.cwd))}`);
|
|
849
|
+
});
|
|
773
850
|
}
|
|
774
|
-
|
|
775
|
-
} catch (err) {
|
|
776
|
-
console.error(red(err.message));
|
|
851
|
+
continue;
|
|
777
852
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
853
|
+
if (trimmed === "/status") {
|
|
854
|
+
await status(current.dir);
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (trimmed === "/history") {
|
|
858
|
+
await history();
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
if (trimmed.startsWith("/feedback ")) {
|
|
862
|
+
const msg = trimmed.slice(10).trim();
|
|
863
|
+
if (msg) await feedback(msg, current.dir);
|
|
864
|
+
else console.log(red("Usage: /feedback <message>"));
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (trimmed.startsWith("/priority ")) {
|
|
868
|
+
const task = trimmed.slice(10).trim();
|
|
869
|
+
if (task) await add(task, 9, current.dir);
|
|
870
|
+
else console.log(red("Usage: /priority <task>"));
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (trimmed === "/queue") {
|
|
874
|
+
await listQueueCmd(current.dir);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (trimmed === "/clear") {
|
|
878
|
+
await clear(current.dir);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (trimmed.startsWith("/")) {
|
|
882
|
+
console.log(red(`Unknown command: ${trimmed.split(" ")[0]}`));
|
|
883
|
+
console.log(dim(" Press Tab to see available commands"));
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
await add(trimmed, 0, current.dir);
|
|
887
|
+
} catch (err) {
|
|
888
|
+
console.error(red(err.message));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
782
891
|
}
|
|
783
892
|
function usage() {
|
|
784
893
|
console.log(`
|