@testany/hephos 0.3.9 → 0.3.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ReplModeInk.d.ts","sourceRoot":"","sources":["../../src/repl/ReplModeInk.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAgoFH,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,QAMjF"}
1
+ {"version":3,"file":"ReplModeInk.d.ts","sourceRoot":"","sources":["../../src/repl/ReplModeInk.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAq3FH,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,QAMjF"}
@@ -25,6 +25,8 @@ import { SessionStorageService } from '@testany/agent-chatter-core';
25
25
  import { RestorePrompt } from './components/RestorePrompt.js';
26
26
  import { QueueDisplay } from './components/QueueDisplay.js';
27
27
  import { TipsBar } from './components/TipsBar.js';
28
+ import { HistoryOverlay } from './components/HistoryOverlay.js';
29
+ import { useHistory } from './hooks/useHistory.js';
28
30
  // Read version from package.json
29
31
  const __filename = fileURLToPath(import.meta.url);
30
32
  const __dirname = path.dirname(__filename);
@@ -230,6 +232,8 @@ function App({ registryPath, debug = false, proxyUrl }) {
230
232
  const [isExiting, setIsExiting] = useState(false);
231
233
  const [queueState, setQueueState] = useState(null);
232
234
  const [showTips, setShowTips] = useState(true); // Tips bar visibility (default: on)
235
+ // Command history management
236
+ const history = useHistory();
233
237
  // Track previous queue size to detect DROP-triggered clears
234
238
  const prevQueueSizeRef = useRef(0);
235
239
  // Streaming event handling
@@ -560,7 +564,31 @@ function App({ registryPath, debug = false, proxyUrl }) {
560
564
  };
561
565
  // Handle input submission (Enter key)
562
566
  const handleInputSubmit = (value) => {
563
- const trimmed = value.trim();
567
+ let submittedValue = value;
568
+ // Exit search mode if active (user submitted with Enter while in search mode)
569
+ // Use the selected history entry as the submitted value for consistency
570
+ if (history.searchMode) {
571
+ const selectedEntry = history.acceptSearch();
572
+ if (selectedEntry !== null) {
573
+ submittedValue = selectedEntry;
574
+ setInput(selectedEntry); // Sync input state
575
+ }
576
+ }
577
+ // Exit history mode if active (user submitted with Enter while browsing history)
578
+ // Use the selected history entry as the submitted value for consistency
579
+ else if (history.isActive) {
580
+ const selectedEntry = history.acceptHistory();
581
+ if (selectedEntry !== null) {
582
+ submittedValue = selectedEntry;
583
+ setInput(selectedEntry); // Sync input state
584
+ }
585
+ }
586
+ const trimmed = submittedValue.trim();
587
+ // Add to history with mode (filtering handled by hook)
588
+ if (trimmed) {
589
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
590
+ history.addEntry(trimmed, historyMode);
591
+ }
564
592
  // Handle / commands
565
593
  if (trimmed.startsWith('/')) {
566
594
  const cmd = trimmed.split(' ')[0].toLowerCase();
@@ -645,8 +673,21 @@ function App({ registryPath, debug = false, proxyUrl }) {
645
673
  }
646
674
  return;
647
675
  }
648
- // ESC key - Cancel agent execution in conversation mode
676
+ // ESC key - Exit search/history mode or cancel agent execution
649
677
  if (key.escape) {
678
+ // First, check if we're in search mode
679
+ if (history.searchMode) {
680
+ const savedInput = history.exitSearchMode();
681
+ setInput(savedInput);
682
+ return;
683
+ }
684
+ // Then check if we're in history browsing mode
685
+ if (history.isActive) {
686
+ const savedInput = history.exitHistory();
687
+ setInput(savedInput);
688
+ return;
689
+ }
690
+ // Cancel agent execution in conversation mode
650
691
  if (mode === 'conversation' && activeCoordinator && executingAgent) {
651
692
  // Check if ESC cancellation is allowed (LLD-05: use uiPrefs)
652
693
  if (uiPrefs.allowEscCancel) {
@@ -660,8 +701,83 @@ function App({ registryPath, debug = false, proxyUrl }) {
660
701
  }
661
702
  }
662
703
  }
704
+ // Ctrl+R - Enter search mode or search backward
705
+ if (key.ctrl && inputChar === 'r') {
706
+ if (mode === 'normal' || mode === 'conversation') {
707
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
708
+ if (history.searchMode) {
709
+ // Already in search mode - search backward
710
+ const result = history.searchBackward();
711
+ if (result !== null) {
712
+ setInput(result);
713
+ }
714
+ }
715
+ else {
716
+ // Enter search mode
717
+ history.enterSearchMode(input, historyMode);
718
+ }
719
+ return;
720
+ }
721
+ }
722
+ // Ctrl+S - Search forward (only in search mode)
723
+ if (key.ctrl && inputChar === 's') {
724
+ if (history.searchMode) {
725
+ const result = history.searchForward();
726
+ if (result !== null) {
727
+ setInput(result);
728
+ }
729
+ return;
730
+ }
731
+ }
732
+ // Ctrl+P - Navigate to previous (older) history entry (same as ↑)
733
+ if (key.ctrl && inputChar === 'p') {
734
+ if (mode === 'normal' || mode === 'conversation') {
735
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
736
+ // In search mode, navigate search results
737
+ if (history.searchMode) {
738
+ const result = history.searchBackward();
739
+ if (result !== null) {
740
+ setInput(result);
741
+ }
742
+ return;
743
+ }
744
+ // Regular history navigation
745
+ const historyEntry = history.navigateUp(input, historyMode);
746
+ if (historyEntry !== null) {
747
+ setInput(historyEntry);
748
+ }
749
+ return;
750
+ }
751
+ }
752
+ // Ctrl+N - Navigate to next (newer) history entry (same as ↓)
753
+ if (key.ctrl && inputChar === 'n') {
754
+ if (mode === 'normal' || mode === 'conversation') {
755
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
756
+ // In search mode, navigate search results
757
+ if (history.searchMode) {
758
+ const result = history.searchForward();
759
+ if (result !== null) {
760
+ setInput(result);
761
+ }
762
+ return;
763
+ }
764
+ // Regular history navigation
765
+ const historyEntry = history.navigateDown(historyMode);
766
+ if (historyEntry !== null) {
767
+ setInput(historyEntry);
768
+ }
769
+ return;
770
+ }
771
+ }
663
772
  // Ctrl+C 退出或取消
664
773
  if (key.ctrl && inputChar === 'c') {
774
+ // First, clean up any search/history state
775
+ if (history.searchMode) {
776
+ history.exitSearchMode();
777
+ }
778
+ else if (history.isActive) {
779
+ history.exitHistory();
780
+ }
665
781
  if (mode === 'conversation' && activeCoordinator) {
666
782
  // 退出对话模式
667
783
  // stop() is async, fire-and-forget for UI responsiveness
@@ -741,14 +857,71 @@ function App({ registryPath, debug = false, proxyUrl }) {
741
857
  // normal/conversation/wizard/form 由 TextInput 的 onSubmit 处理
742
858
  return;
743
859
  }
860
+ // Alt+↑ - Large jump backward (skip 5 entries)
861
+ // Cross-platform: key.meta captures Option key on macOS, Alt key on Windows/Linux
862
+ // in most modern terminal emulators (Windows Terminal, iTerm2, etc.)
863
+ if (key.meta && key.upArrow) {
864
+ if ((mode === 'normal' || mode === 'conversation') && !history.searchMode) {
865
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
866
+ const historyEntry = history.jumpUp(input, historyMode);
867
+ if (historyEntry !== null) {
868
+ setInput(historyEntry);
869
+ }
870
+ return;
871
+ }
872
+ }
873
+ // Alt+↓ - Large jump forward (skip 5 entries)
874
+ // Cross-platform: key.meta captures Option key on macOS, Alt key on Windows/Linux
875
+ if (key.meta && key.downArrow) {
876
+ if ((mode === 'normal' || mode === 'conversation') && !history.searchMode && history.isActive) {
877
+ const historyMode = mode === 'conversation' ? 'conversation' : 'normal';
878
+ const historyEntry = history.jumpDown(historyMode);
879
+ if (historyEntry !== null) {
880
+ setInput(historyEntry);
881
+ }
882
+ return;
883
+ }
884
+ }
744
885
  // 处理不同模式下的上下键导航
745
886
  if (key.upArrow) {
887
+ // Search mode takes priority - navigate search results
888
+ if (history.searchMode) {
889
+ const result = history.searchBackward();
890
+ if (result !== null) {
891
+ setInput(result);
892
+ }
893
+ return;
894
+ }
746
895
  if (mode === 'normal') {
896
+ // If already in history mode, continue history navigation (disable command hints)
897
+ if (history.isActive) {
898
+ const historyEntry = history.navigateUp(input, 'normal');
899
+ if (historyEntry !== null) {
900
+ setInput(historyEntry);
901
+ }
902
+ return;
903
+ }
904
+ // Not in history mode - check for command hints first
747
905
  const matches = getMatches();
748
906
  if (matches.length > 0) {
907
+ // Command hint navigation has priority when not in history mode
749
908
  setSelectedIndex(prev => (prev > 0 ? prev - 1 : matches.length - 1));
750
909
  return;
751
910
  }
911
+ // No command hints - enter history mode
912
+ const historyEntry = history.navigateUp(input, 'normal');
913
+ if (historyEntry !== null) {
914
+ setInput(historyEntry);
915
+ }
916
+ return;
917
+ }
918
+ else if (mode === 'conversation') {
919
+ // Regular history navigation in conversation mode
920
+ const historyEntry = history.navigateUp(input, 'conversation');
921
+ if (historyEntry !== null) {
922
+ setInput(historyEntry);
923
+ }
924
+ return;
752
925
  }
753
926
  else if (mode === 'menu' && menuItems.length > 0) {
754
927
  setSelectedIndex(prev => (prev > 0 ? prev - 1 : menuItems.length - 1));
@@ -760,12 +933,43 @@ function App({ registryPath, debug = false, proxyUrl }) {
760
933
  }
761
934
  }
762
935
  if (key.downArrow) {
936
+ // Search mode takes priority - navigate search results
937
+ if (history.searchMode) {
938
+ const result = history.searchForward();
939
+ if (result !== null) {
940
+ setInput(result);
941
+ }
942
+ return;
943
+ }
763
944
  if (mode === 'normal') {
945
+ // If already in history mode, continue history navigation (disable command hints)
946
+ if (history.isActive) {
947
+ const historyEntry = history.navigateDown('normal');
948
+ if (historyEntry !== null) {
949
+ setInput(historyEntry);
950
+ }
951
+ return;
952
+ }
953
+ // Not in history mode - check for command hints first
764
954
  const matches = getMatches();
765
955
  if (matches.length > 0) {
956
+ // Command hint navigation has priority when not in history mode
766
957
  setSelectedIndex(prev => (prev < matches.length - 1 ? prev + 1 : 0));
767
958
  return;
768
959
  }
960
+ // No command hints and not in history mode - ↓ does nothing
961
+ // (history mode is entered via ↑, not ↓)
962
+ return;
963
+ }
964
+ else if (mode === 'conversation') {
965
+ // If in history mode, continue navigation; otherwise ↓ does nothing
966
+ if (history.isActive) {
967
+ const historyEntry = history.navigateDown('conversation');
968
+ if (historyEntry !== null) {
969
+ setInput(historyEntry);
970
+ }
971
+ }
972
+ return;
769
973
  }
770
974
  else if (mode === 'menu' && menuItems.length > 0) {
771
975
  setSelectedIndex(prev => (prev < menuItems.length - 1 ? prev + 1 : 0));
@@ -1620,6 +1824,13 @@ function App({ registryPath, debug = false, proxyUrl }) {
1620
1824
  }
1621
1825
  }
1622
1826
  }
1827
+ // Show search/history mode indicator in conversation mode
1828
+ if (history.searchMode) {
1829
+ return _jsxs(Text, { color: "yellow", children: ["[search:", teamName, "] ", history.searchQuery ? `"${history.searchQuery}"` : '', "> "] });
1830
+ }
1831
+ if (history.isActive) {
1832
+ return _jsxs(Text, { color: "green", bold: true, children: ["[history:", teamName, "] ", memberPrompt, "> "] });
1833
+ }
1623
1834
  return _jsxs(Text, { color: "green", bold: true, children: ["[conversation:", teamName, "] ", memberPrompt, "> "] });
1624
1835
  }
1625
1836
  case 'wizard':
@@ -1627,10 +1838,16 @@ function App({ registryPath, debug = false, proxyUrl }) {
1627
1838
  case 'form':
1628
1839
  return _jsx(Text, { color: "cyan", bold: true, children: "[form] input> " });
1629
1840
  default:
1630
- // 'normal' mode
1841
+ // 'normal' mode - show search/history mode indicator
1842
+ if (history.searchMode) {
1843
+ return _jsxs(Text, { color: "yellow", children: ["[search] ", history.searchQuery ? `"${history.searchQuery}"` : '', "> "] });
1844
+ }
1845
+ if (history.isActive) {
1846
+ return _jsx(Text, { color: "cyan", children: "[history] hephos> " });
1847
+ }
1631
1848
  return _jsx(Text, { color: "cyan", children: "[normal] hephos> " });
1632
1849
  }
1633
- })(), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: handleInputSubmit, placeholder: " " })] }), _jsx(Text, { color: mode === 'conversation' ? 'green' : 'cyan', dimColor: true, children: '─'.repeat(terminalWidth - 4) })] })), !isExiting && mode === 'normal' && _jsx(CommandHints, { input: input, selectedIndex: selectedIndex }), !isExiting && mode === 'conversation' && (_jsx(TipsBar, { visible: showTips, terminalWidth: terminalWidth }))] }));
1850
+ })(), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: handleInputSubmit, placeholder: " " })] }), _jsx(Text, { color: mode === 'conversation' ? 'green' : 'cyan', dimColor: true, children: '─'.repeat(terminalWidth - 4) })] })), !isExiting && (mode === 'normal' || mode === 'conversation') && (history.isActive || history.searchMode) && (_jsx(HistoryOverlay, { visible: true, entries: history.entries, currentIndex: history.currentIndex, activeMode: history.activeMode, searchMode: history.searchMode, searchQuery: history.searchQuery, terminalWidth: terminalWidth })), !isExiting && mode === 'normal' && !history.isActive && !history.searchMode && _jsx(CommandHints, { input: input, selectedIndex: selectedIndex }), !isExiting && mode === 'conversation' && !history.isActive && !history.searchMode && (_jsx(TipsBar, { visible: showTips, terminalWidth: terminalWidth }))] }));
1634
1851
  }
1635
1852
  export function startReplInk(registryPath, options = {}) {
1636
1853
  render(_jsx(App, { registryPath: registryPath, debug: options.debug, proxyUrl: options.proxyUrl }), {