wave-code 0.2.0 → 0.4.0

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.
Files changed (140) hide show
  1. package/dist/commands/plugin/disable.d.ts +2 -1
  2. package/dist/commands/plugin/disable.d.ts.map +1 -1
  3. package/dist/commands/plugin/disable.js +3 -2
  4. package/dist/commands/plugin/enable.d.ts +2 -1
  5. package/dist/commands/plugin/enable.d.ts.map +1 -1
  6. package/dist/commands/plugin/enable.js +3 -2
  7. package/dist/commands/plugin/install.d.ts +2 -1
  8. package/dist/commands/plugin/install.d.ts.map +1 -1
  9. package/dist/commands/plugin/list.d.ts.map +1 -1
  10. package/dist/commands/plugin/list.js +15 -3
  11. package/dist/commands/plugin/marketplace.d.ts +3 -0
  12. package/dist/commands/plugin/marketplace.d.ts.map +1 -1
  13. package/dist/commands/plugin/marketplace.js +15 -1
  14. package/dist/commands/plugin/uninstall.d.ts +4 -0
  15. package/dist/commands/plugin/uninstall.d.ts.map +1 -0
  16. package/dist/commands/plugin/uninstall.js +29 -0
  17. package/dist/commands/plugin/update.d.ts +4 -0
  18. package/dist/commands/plugin/update.d.ts.map +1 -0
  19. package/dist/commands/plugin/update.js +15 -0
  20. package/dist/components/ChatInterface.d.ts.map +1 -1
  21. package/dist/components/ChatInterface.js +2 -2
  22. package/dist/components/CommandSelector.d.ts.map +1 -1
  23. package/dist/components/CommandSelector.js +6 -0
  24. package/dist/components/Confirmation.js +1 -1
  25. package/dist/components/DiscoverView.d.ts +3 -0
  26. package/dist/components/DiscoverView.d.ts.map +1 -0
  27. package/dist/components/DiscoverView.js +25 -0
  28. package/dist/components/FileSelector.js +1 -1
  29. package/dist/components/HistorySearch.d.ts +8 -0
  30. package/dist/components/HistorySearch.d.ts.map +1 -0
  31. package/dist/components/HistorySearch.js +67 -0
  32. package/dist/components/InputBox.d.ts +1 -1
  33. package/dist/components/InputBox.d.ts.map +1 -1
  34. package/dist/components/InputBox.js +26 -17
  35. package/dist/components/InstalledView.d.ts +3 -0
  36. package/dist/components/InstalledView.d.ts.map +1 -0
  37. package/dist/components/InstalledView.js +30 -0
  38. package/dist/components/Markdown.d.ts.map +1 -1
  39. package/dist/components/Markdown.js +22 -9
  40. package/dist/components/MarketplaceAddForm.d.ts +3 -0
  41. package/dist/components/MarketplaceAddForm.d.ts.map +1 -0
  42. package/dist/components/MarketplaceAddForm.js +26 -0
  43. package/dist/components/MarketplaceDetail.d.ts +3 -0
  44. package/dist/components/MarketplaceDetail.d.ts.map +1 -0
  45. package/dist/components/MarketplaceDetail.js +38 -0
  46. package/dist/components/MarketplaceList.d.ts +9 -0
  47. package/dist/components/MarketplaceList.d.ts.map +1 -0
  48. package/dist/components/MarketplaceList.js +16 -0
  49. package/dist/components/MarketplaceView.d.ts +3 -0
  50. package/dist/components/MarketplaceView.d.ts.map +1 -0
  51. package/dist/components/MarketplaceView.js +28 -0
  52. package/dist/components/PluginDetail.d.ts +3 -0
  53. package/dist/components/PluginDetail.d.ts.map +1 -0
  54. package/dist/components/PluginDetail.js +63 -0
  55. package/dist/components/PluginList.d.ts +14 -0
  56. package/dist/components/PluginList.d.ts.map +1 -0
  57. package/dist/components/PluginList.js +12 -0
  58. package/dist/components/PluginManagerShell.d.ts +5 -0
  59. package/dist/components/PluginManagerShell.d.ts.map +1 -0
  60. package/dist/components/PluginManagerShell.js +89 -0
  61. package/dist/components/PluginManagerTypes.d.ts +33 -0
  62. package/dist/components/PluginManagerTypes.d.ts.map +1 -0
  63. package/dist/components/PluginManagerTypes.js +1 -0
  64. package/dist/components/RewindCommand.d.ts +9 -0
  65. package/dist/components/RewindCommand.d.ts.map +1 -0
  66. package/dist/components/RewindCommand.js +42 -0
  67. package/dist/components/SessionSelector.d.ts +11 -0
  68. package/dist/components/SessionSelector.d.ts.map +1 -0
  69. package/dist/components/SessionSelector.js +38 -0
  70. package/dist/components/SubagentBlock.d.ts.map +1 -1
  71. package/dist/components/SubagentBlock.js +20 -1
  72. package/dist/components/ToolResultDisplay.js +1 -1
  73. package/dist/contexts/PluginManagerContext.d.ts +4 -0
  74. package/dist/contexts/PluginManagerContext.d.ts.map +1 -0
  75. package/dist/contexts/PluginManagerContext.js +9 -0
  76. package/dist/contexts/useChat.d.ts +2 -0
  77. package/dist/contexts/useChat.d.ts.map +1 -1
  78. package/dist/contexts/useChat.js +21 -0
  79. package/dist/hooks/useInputManager.d.ts +6 -14
  80. package/dist/hooks/useInputManager.d.ts.map +1 -1
  81. package/dist/hooks/useInputManager.js +29 -45
  82. package/dist/hooks/usePluginManager.d.ts +3 -0
  83. package/dist/hooks/usePluginManager.d.ts.map +1 -0
  84. package/dist/hooks/usePluginManager.js +223 -0
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +150 -177
  87. package/dist/managers/InputManager.d.ts +12 -21
  88. package/dist/managers/InputManager.d.ts.map +1 -1
  89. package/dist/managers/InputManager.js +77 -108
  90. package/dist/plugin-manager-cli.d.ts +6 -0
  91. package/dist/plugin-manager-cli.d.ts.map +1 -0
  92. package/dist/plugin-manager-cli.js +12 -0
  93. package/dist/session-selector-cli.d.ts +2 -0
  94. package/dist/session-selector-cli.d.ts.map +1 -0
  95. package/dist/session-selector-cli.js +25 -0
  96. package/package.json +7 -3
  97. package/src/commands/plugin/disable.ts +7 -3
  98. package/src/commands/plugin/enable.ts +7 -3
  99. package/src/commands/plugin/install.ts +2 -1
  100. package/src/commands/plugin/list.ts +21 -3
  101. package/src/commands/plugin/marketplace.ts +17 -1
  102. package/src/commands/plugin/uninstall.ts +39 -0
  103. package/src/commands/plugin/update.ts +19 -0
  104. package/src/components/ChatInterface.tsx +2 -1
  105. package/src/components/CommandSelector.tsx +7 -0
  106. package/src/components/Confirmation.tsx +1 -1
  107. package/src/components/DiscoverView.tsx +31 -0
  108. package/src/components/FileSelector.tsx +1 -1
  109. package/src/components/HistorySearch.tsx +148 -0
  110. package/src/components/InputBox.tsx +43 -28
  111. package/src/components/InstalledView.tsx +61 -0
  112. package/src/components/Markdown.tsx +37 -26
  113. package/src/components/MarketplaceAddForm.tsx +39 -0
  114. package/src/components/MarketplaceDetail.tsx +79 -0
  115. package/src/components/MarketplaceList.tsx +52 -0
  116. package/src/components/MarketplaceView.tsx +43 -0
  117. package/src/components/PluginDetail.tsx +147 -0
  118. package/src/components/PluginList.tsx +51 -0
  119. package/src/components/PluginManagerShell.tsx +189 -0
  120. package/src/components/PluginManagerTypes.ts +47 -0
  121. package/src/components/RewindCommand.tsx +114 -0
  122. package/src/components/SessionSelector.tsx +127 -0
  123. package/src/components/SubagentBlock.tsx +29 -1
  124. package/src/components/ToolResultDisplay.tsx +2 -2
  125. package/src/contexts/PluginManagerContext.ts +15 -0
  126. package/src/contexts/useChat.tsx +26 -0
  127. package/src/hooks/useInputManager.ts +29 -61
  128. package/src/hooks/usePluginManager.ts +296 -0
  129. package/src/index.ts +241 -280
  130. package/src/managers/InputManager.ts +93 -149
  131. package/src/plugin-manager-cli.tsx +13 -0
  132. package/src/session-selector-cli.tsx +37 -0
  133. package/dist/components/BashHistorySelector.d.ts +0 -11
  134. package/dist/components/BashHistorySelector.d.ts.map +0 -1
  135. package/dist/components/BashHistorySelector.js +0 -93
  136. package/dist/hooks/usePagination.d.ts +0 -20
  137. package/dist/hooks/usePagination.d.ts.map +0 -1
  138. package/dist/hooks/usePagination.js +0 -168
  139. package/src/components/BashHistorySelector.tsx +0 -181
  140. package/src/hooks/usePagination.ts +0 -203
@@ -1,4 +1,4 @@
1
- import { searchFiles as searchFilesUtil, deleteBashCommandFromHistory, } from "wave-agent-sdk";
1
+ import { searchFiles as searchFilesUtil, PromptHistoryManager, } from "wave-agent-sdk";
2
2
  import { readClipboardImage } from "../utils/clipboard.js";
3
3
  export class InputManager {
4
4
  constructor(callbacks = {}) {
@@ -15,10 +15,9 @@ export class InputManager {
15
15
  this.showCommandSelector = false;
16
16
  this.slashPosition = -1;
17
17
  this.commandSearchQuery = "";
18
- // Bash history selector state
19
- this.showBashHistorySelector = false;
20
- this.exclamationPosition = -1;
21
- this.bashHistorySearchQuery = "";
18
+ // History search state
19
+ this.showHistorySearch = false;
20
+ this.historySearchQuery = "";
22
21
  // Memory type selector state
23
22
  this.showMemoryTypeSelector = false;
24
23
  this.memoryMessage = "";
@@ -40,6 +39,7 @@ export class InputManager {
40
39
  // Additional UI state
41
40
  this.showBashManager = false;
42
41
  this.showMcpManager = false;
42
+ this.showRewindManager = false;
43
43
  // Permission mode state
44
44
  this.permissionMode = "default";
45
45
  // Flag to prevent handleInput conflicts when selector selection occurs
@@ -228,6 +228,11 @@ export class InputManager {
228
228
  this.callbacks.onShowMcpManager();
229
229
  commandExecuted = true;
230
230
  }
231
+ else if (command === "rewind" &&
232
+ this.callbacks.onShowRewindManager) {
233
+ this.callbacks.onShowRewindManager();
234
+ commandExecuted = true;
235
+ }
231
236
  }
232
237
  })();
233
238
  this.handleCancelCommandSelect();
@@ -275,72 +280,6 @@ export class InputManager {
275
280
  }
276
281
  return false;
277
282
  }
278
- // Bash history selector methods
279
- activateBashHistorySelector(position) {
280
- this.showBashHistorySelector = true;
281
- this.exclamationPosition = position;
282
- this.bashHistorySearchQuery = "";
283
- this.callbacks.onBashHistorySelectorStateChange?.(true, "", position);
284
- }
285
- updateBashHistorySearchQuery(query) {
286
- this.bashHistorySearchQuery = query;
287
- this.callbacks.onBashHistorySelectorStateChange?.(this.showBashHistorySelector, query, this.exclamationPosition);
288
- }
289
- handleBashHistorySelect(command) {
290
- if (this.exclamationPosition >= 0) {
291
- const beforeExclamation = this.inputText.substring(0, this.exclamationPosition);
292
- const afterQuery = this.inputText.substring(this.cursorPosition);
293
- const newInput = beforeExclamation + `!${command}` + afterQuery;
294
- const newCursorPosition = beforeExclamation.length + command.length + 1;
295
- this.inputText = newInput;
296
- this.cursorPosition = newCursorPosition;
297
- this.handleCancelBashHistorySelect();
298
- // Set flag to prevent handleInput from processing the same Enter key
299
- this.selectorJustUsed = true;
300
- setTimeout(() => {
301
- this.selectorJustUsed = false;
302
- }, 0);
303
- this.callbacks.onInputTextChange?.(newInput);
304
- this.callbacks.onCursorPositionChange?.(newCursorPosition);
305
- return { newInput, newCursorPosition };
306
- }
307
- return { newInput: this.inputText, newCursorPosition: this.cursorPosition };
308
- }
309
- handleCancelBashHistorySelect() {
310
- this.showBashHistorySelector = false;
311
- this.exclamationPosition = -1;
312
- this.bashHistorySearchQuery = "";
313
- this.callbacks.onBashHistorySelectorStateChange?.(false, "", -1);
314
- }
315
- handleBashHistoryExecute(command) {
316
- this.showBashHistorySelector = false;
317
- this.exclamationPosition = -1;
318
- this.bashHistorySearchQuery = "";
319
- this.callbacks.onBashHistorySelectorStateChange?.(false, "", -1);
320
- return command; // Return command to execute
321
- }
322
- handleBashHistoryExecuteAndSend(command) {
323
- const commandToExecute = this.handleBashHistoryExecute(command);
324
- // Clear input box and execute command, ensure command starts with !
325
- const bashCommand = commandToExecute.startsWith("!")
326
- ? commandToExecute
327
- : `!${commandToExecute}`;
328
- this.clearInput();
329
- this.callbacks.onSendMessage?.(bashCommand);
330
- }
331
- handleBashHistoryDelete(command, workdir) {
332
- deleteBashCommandFromHistory(command, workdir);
333
- // Trigger a refresh of the selector state to force re-render of BashHistorySelector
334
- this.callbacks.onBashHistorySelectorStateChange?.(this.showBashHistorySelector, this.bashHistorySearchQuery, this.exclamationPosition);
335
- }
336
- checkForExclamationDeletion(cursorPosition) {
337
- if (this.showBashHistorySelector &&
338
- cursorPosition <= this.exclamationPosition) {
339
- this.handleCancelBashHistorySelect();
340
- return true;
341
- }
342
- return false;
343
- }
344
283
  // Memory type selector methods
345
284
  activateMemoryTypeSelector(message) {
346
285
  this.showMemoryTypeSelector = true;
@@ -421,9 +360,6 @@ export class InputManager {
421
360
  isCommandSelectorActive() {
422
361
  return this.showCommandSelector;
423
362
  }
424
- isBashHistorySelectorActive() {
425
- return this.showBashHistorySelector;
426
- }
427
363
  isMemoryTypeSelectorActive() {
428
364
  return this.showMemoryTypeSelector;
429
365
  }
@@ -442,13 +378,6 @@ export class InputManager {
442
378
  position: this.slashPosition,
443
379
  };
444
380
  }
445
- getBashHistorySelectorState() {
446
- return {
447
- show: this.showBashHistorySelector,
448
- query: this.bashHistorySearchQuery,
449
- position: this.exclamationPosition,
450
- };
451
- }
452
381
  getMemoryTypeSelectorState() {
453
382
  return {
454
383
  show: this.showMemoryTypeSelector,
@@ -469,25 +398,19 @@ export class InputManager {
469
398
  const newQuery = inputText.substring(queryStart, queryEnd);
470
399
  this.updateCommandSearchQuery(newQuery);
471
400
  }
472
- else if (this.showBashHistorySelector && this.exclamationPosition >= 0) {
473
- const queryStart = this.exclamationPosition + 1;
474
- const queryEnd = cursorPosition;
475
- const newQuery = inputText.substring(queryStart, queryEnd);
476
- this.updateBashHistorySearchQuery(newQuery);
477
- }
478
401
  }
479
402
  // Handle special character input that might trigger selectors
480
403
  handleSpecialCharInput(char) {
481
404
  if (char === "@") {
482
405
  this.activateFileSelector(this.cursorPosition - 1);
483
406
  }
484
- else if (char === "/" && !this.showFileSelector) {
407
+ else if (char === "/" &&
408
+ !this.showFileSelector &&
409
+ this.cursorPosition === 1) {
485
410
  // Don't activate command selector when file selector is active
411
+ // Only activate command selector if '/' is at the start of input
486
412
  this.activateCommandSelector(this.cursorPosition - 1);
487
413
  }
488
- else if (char === "!" && this.cursorPosition === 1) {
489
- this.activateBashHistorySelector(0);
490
- }
491
414
  else if (char === "#" && this.cursorPosition === 1) {
492
415
  // Memory message detection will be handled in submit
493
416
  }
@@ -630,6 +553,13 @@ export class InputManager {
630
553
  this.showMcpManager = show;
631
554
  this.callbacks.onMcpManagerStateChange?.(show);
632
555
  }
556
+ getShowRewindManager() {
557
+ return this.showRewindManager;
558
+ }
559
+ setShowRewindManager(show) {
560
+ this.showRewindManager = show;
561
+ this.callbacks.onRewindManagerStateChange?.(show);
562
+ }
633
563
  // Permission mode methods
634
564
  getPermissionMode() {
635
565
  return this.permissionMode;
@@ -676,6 +606,10 @@ export class InputManager {
676
606
  // Remove image placeholders, expand long text placeholders, send message
677
607
  let cleanContent = this.inputText.replace(imageRegex, "").trim();
678
608
  cleanContent = this.expandLongTextPlaceholders(cleanContent);
609
+ // Save to prompt history
610
+ PromptHistoryManager.addEntry(cleanContent).catch((err) => {
611
+ this.logger?.error("Failed to save prompt history", err);
612
+ });
679
613
  this.callbacks.onSendMessage?.(cleanContent, referencedImages.length > 0 ? referencedImages : undefined);
680
614
  this.clearInput();
681
615
  this.callbacks.onResetHistoryNavigation?.();
@@ -691,7 +625,6 @@ export class InputManager {
691
625
  // Check for special character deletion
692
626
  this.checkForAtDeletion(newCursorPosition);
693
627
  this.checkForSlashDeletion(newCursorPosition);
694
- this.checkForExclamationDeletion(newCursorPosition);
695
628
  // Update search queries using the same logic as character input
696
629
  this.updateSearchQueriesForActiveSelectors(newInput, newCursorPosition);
697
630
  });
@@ -724,6 +657,28 @@ export class InputManager {
724
657
  }
725
658
  return false;
726
659
  }
660
+ // History search methods
661
+ activateHistorySearch() {
662
+ this.showHistorySearch = true;
663
+ this.historySearchQuery = "";
664
+ this.callbacks.onHistorySearchStateChange?.(true, "");
665
+ }
666
+ updateHistorySearchQuery(query) {
667
+ this.historySearchQuery = query;
668
+ this.callbacks.onHistorySearchStateChange?.(true, query);
669
+ }
670
+ handleHistorySearchSelect(prompt) {
671
+ this.inputText = prompt;
672
+ this.cursorPosition = prompt.length;
673
+ this.callbacks.onInputTextChange?.(prompt);
674
+ this.callbacks.onCursorPositionChange?.(prompt.length);
675
+ this.handleCancelHistorySearch();
676
+ }
677
+ handleCancelHistorySearch() {
678
+ this.showHistorySearch = false;
679
+ this.historySearchQuery = "";
680
+ this.callbacks.onHistorySearchStateChange?.(false, "");
681
+ }
727
682
  // Handle normal input (when no selector is active)
728
683
  async handleNormalInput(input, key, attachedImages, isLoading = false, isCommandRunning = false, clearImages) {
729
684
  if (key.return) {
@@ -738,9 +693,6 @@ export class InputManager {
738
693
  else if (this.showCommandSelector) {
739
694
  this.handleCancelCommandSelect();
740
695
  }
741
- else if (this.showBashHistorySelector) {
742
- this.handleCancelBashHistorySelect();
743
- }
744
696
  return true;
745
697
  }
746
698
  if (key.backspace || key.delete) {
@@ -751,7 +703,6 @@ export class InputManager {
751
703
  const newCursorPosition = this.cursorPosition - 1;
752
704
  this.checkForAtDeletion(newCursorPosition);
753
705
  this.checkForSlashDeletion(newCursorPosition);
754
- this.checkForExclamationDeletion(newCursorPosition);
755
706
  }
756
707
  return true;
757
708
  }
@@ -778,18 +729,17 @@ export class InputManager {
778
729
  });
779
730
  return true;
780
731
  }
732
+ // Handle Ctrl+R for history search
733
+ if (key.ctrl && input === "r") {
734
+ this.activateHistorySearch();
735
+ return true;
736
+ }
781
737
  // Handle up/down keys for history navigation (only when no selector is active)
782
- if (key.upArrow &&
783
- !this.showFileSelector &&
784
- !this.showCommandSelector &&
785
- !this.showBashHistorySelector) {
738
+ if (key.upArrow && !this.showFileSelector && !this.showCommandSelector) {
786
739
  this.navigateHistory("up", this.inputText);
787
740
  return true;
788
741
  }
789
- if (key.downArrow &&
790
- !this.showFileSelector &&
791
- !this.showCommandSelector &&
792
- !this.showBashHistorySelector) {
742
+ if (key.downArrow && !this.showFileSelector && !this.showCommandSelector) {
793
743
  this.navigateHistory("down", this.inputText);
794
744
  return true;
795
745
  }
@@ -832,16 +782,35 @@ export class InputManager {
832
782
  // Check if any selector is active
833
783
  if (this.showFileSelector ||
834
784
  this.showCommandSelector ||
835
- this.showBashHistorySelector ||
785
+ this.showHistorySearch ||
836
786
  this.showMemoryTypeSelector ||
837
787
  this.showBashManager ||
838
- this.showMcpManager) {
788
+ this.showMcpManager ||
789
+ this.showRewindManager) {
839
790
  if (this.showMemoryTypeSelector ||
840
791
  this.showBashManager ||
841
- this.showMcpManager) {
842
- // Memory type selector, bash manager and MCP manager don't need to handle input, handled by component itself
792
+ this.showMcpManager ||
793
+ this.showRewindManager) {
794
+ // Memory type selector, bash manager, MCP manager and Rewind don't need to handle input, handled by component itself
843
795
  return false;
844
796
  }
797
+ if (this.showHistorySearch) {
798
+ if (key.escape) {
799
+ this.handleCancelHistorySearch();
800
+ return true;
801
+ }
802
+ if (key.backspace || key.delete) {
803
+ if (this.historySearchQuery.length > 0) {
804
+ this.updateHistorySearchQuery(this.historySearchQuery.slice(0, -1));
805
+ }
806
+ return true;
807
+ }
808
+ if (input && !key.ctrl && !key.meta && !key.return && !key.tab) {
809
+ this.updateHistorySearchQuery(this.historySearchQuery + input);
810
+ return true;
811
+ }
812
+ return true; // Let HistorySearch component handle arrows and Enter
813
+ }
845
814
  return this.handleSelectorInput(input, key);
846
815
  }
847
816
  else {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Entry point for the Plugin Manager CLI.
3
+ * Renders the Ink component and handles the lifecycle.
4
+ */
5
+ export declare function startPluginManagerCli(): Promise<boolean>;
6
+ //# sourceMappingURL=plugin-manager-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-manager-cli.d.ts","sourceRoot":"","sources":["../src/plugin-manager-cli.tsx"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAI9D"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from "ink";
3
+ import { PluginManagerShell } from "./components/PluginManagerShell.js";
4
+ /**
5
+ * Entry point for the Plugin Manager CLI.
6
+ * Renders the Ink component and handles the lifecycle.
7
+ */
8
+ export async function startPluginManagerCli() {
9
+ const { waitUntilExit } = render(_jsx(PluginManagerShell, {}));
10
+ await waitUntilExit();
11
+ return true;
12
+ }
@@ -0,0 +1,2 @@
1
+ export declare function startSessionSelectorCli(): Promise<string | null>;
2
+ //# sourceMappingURL=session-selector-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-selector-cli.d.ts","sourceRoot":"","sources":["../src/session-selector-cli.tsx"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BtE"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, Box } from "ink";
3
+ import { listSessions, truncateContent } from "wave-agent-sdk";
4
+ import { SessionSelector } from "./components/SessionSelector.js";
5
+ export async function startSessionSelectorCli() {
6
+ const currentWorkdir = process.cwd();
7
+ const sessions = await listSessions(currentWorkdir);
8
+ if (sessions.length === 0) {
9
+ console.log(`No sessions found for workdir: ${currentWorkdir}`);
10
+ return null;
11
+ }
12
+ const sessionsWithContent = sessions.map((s) => ({
13
+ ...s,
14
+ firstMessage: truncateContent(s.firstMessage || "No content", 80),
15
+ }));
16
+ return new Promise((resolve) => {
17
+ const { unmount } = render(_jsx(Box, { padding: 1, children: _jsx(SessionSelector, { sessions: sessionsWithContent, onSelect: (sessionId) => {
18
+ unmount();
19
+ resolve(sessionId);
20
+ }, onCancel: () => {
21
+ unmount();
22
+ resolve(null);
23
+ } }) }));
24
+ });
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,17 +30,20 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "chalk": "^5.6.2",
33
+ "cli-highlight": "^2.1.11",
33
34
  "diff": "^8.0.2",
34
35
  "glob": "^13.0.0",
35
36
  "ink": "^6.5.1",
36
37
  "marked": "^11.2.0",
37
- "react": "^19.1.0",
38
+ "react": "^19.2.4",
39
+ "react-dom": "19.2.4",
38
40
  "yargs": "^17.7.2",
39
- "wave-agent-sdk": "0.2.0"
41
+ "wave-agent-sdk": "0.4.0"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/react": "^19.1.8",
43
45
  "@types/yargs": "^17.0.0",
46
+ "@vitest/coverage-v8": "^3.2.4",
44
47
  "eslint-plugin-react": "^7.37.5",
45
48
  "eslint-plugin-react-hooks": "^5.2.0",
46
49
  "ink-testing-library": "^4.0.0",
@@ -62,6 +65,7 @@
62
65
  "type-check": "tsc --noEmit --incremental",
63
66
  "watch": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
64
67
  "test": "vitest run",
68
+ "test:coverage": "vitest run --coverage",
65
69
  "lint": "eslint --cache",
66
70
  "format": "prettier --write .",
67
71
  "version:patch": "node ../../scripts/version.js patch",
@@ -2,11 +2,12 @@ import {
2
2
  ConfigurationService,
3
3
  PluginManager,
4
4
  PluginScopeManager,
5
+ Scope,
5
6
  } from "wave-agent-sdk";
6
7
 
7
8
  export async function disablePluginCommand(argv: {
8
9
  plugin: string;
9
- scope: "user" | "project" | "local";
10
+ scope?: Scope;
10
11
  }) {
11
12
  const workdir = process.cwd();
12
13
  const configurationService = new ConfigurationService();
@@ -17,10 +18,13 @@ export async function disablePluginCommand(argv: {
17
18
  pluginManager,
18
19
  });
19
20
 
21
+ const scope =
22
+ argv.scope || scopeManager.findPluginScope(argv.plugin) || "user";
23
+
20
24
  try {
21
- await scopeManager.disablePlugin(argv.scope, argv.plugin);
25
+ await scopeManager.disablePlugin(scope, argv.plugin);
22
26
  console.log(
23
- `Successfully disabled plugin: ${argv.plugin} in ${argv.scope} scope`,
27
+ `Successfully disabled plugin: ${argv.plugin} in ${scope} scope`,
24
28
  );
25
29
  process.exit(0);
26
30
  } catch (error) {
@@ -2,11 +2,12 @@ import {
2
2
  ConfigurationService,
3
3
  PluginManager,
4
4
  PluginScopeManager,
5
+ Scope,
5
6
  } from "wave-agent-sdk";
6
7
 
7
8
  export async function enablePluginCommand(argv: {
8
9
  plugin: string;
9
- scope: "user" | "project" | "local";
10
+ scope?: Scope;
10
11
  }) {
11
12
  const workdir = process.cwd();
12
13
  const configurationService = new ConfigurationService();
@@ -17,10 +18,13 @@ export async function enablePluginCommand(argv: {
17
18
  pluginManager,
18
19
  });
19
20
 
21
+ const scope =
22
+ argv.scope || scopeManager.findPluginScope(argv.plugin) || "user";
23
+
20
24
  try {
21
- await scopeManager.enablePlugin(argv.scope, argv.plugin);
25
+ await scopeManager.enablePlugin(scope, argv.plugin);
22
26
  console.log(
23
- `Successfully enabled plugin: ${argv.plugin} in ${argv.scope} scope`,
27
+ `Successfully enabled plugin: ${argv.plugin} in ${scope} scope`,
24
28
  );
25
29
  process.exit(0);
26
30
  } catch (error) {
@@ -3,11 +3,12 @@ import {
3
3
  ConfigurationService,
4
4
  PluginManager,
5
5
  PluginScopeManager,
6
+ Scope,
6
7
  } from "wave-agent-sdk";
7
8
 
8
9
  export async function installPluginCommand(argv: {
9
10
  plugin: string;
10
- scope?: "user" | "project" | "local";
11
+ scope?: Scope;
11
12
  }) {
12
13
  const marketplaceService = new MarketplaceService();
13
14
  const workdir = process.cwd();
@@ -1,9 +1,23 @@
1
- import { MarketplaceService, ConfigurationService } from "wave-agent-sdk";
1
+ import {
2
+ MarketplaceService,
3
+ ConfigurationService,
4
+ PluginScopeManager,
5
+ PluginManager,
6
+ } from "wave-agent-sdk";
2
7
 
3
8
  export async function listPluginsCommand() {
4
- const marketplaceService = new MarketplaceService();
5
9
  const configurationService = new ConfigurationService();
10
+ const marketplaceService = new MarketplaceService();
6
11
  const workdir = process.cwd();
12
+ const pluginManager = new PluginManager({
13
+ workdir,
14
+ configurationService,
15
+ });
16
+ const pluginScopeManager = new PluginScopeManager({
17
+ workdir,
18
+ configurationService,
19
+ pluginManager,
20
+ });
7
21
 
8
22
  try {
9
23
  const installedPlugins = await marketplaceService.getInstalledPlugins();
@@ -16,6 +30,7 @@ export async function listPluginsCommand() {
16
30
  marketplace: string;
17
31
  installed: boolean;
18
32
  version?: string;
33
+ scope?: string;
19
34
  }[] = [];
20
35
 
21
36
  for (const m of marketplaces) {
@@ -24,6 +39,7 @@ export async function listPluginsCommand() {
24
39
  marketplaceService.getMarketplacePath(m),
25
40
  );
26
41
  manifest.plugins.forEach((p) => {
42
+ const pluginId = `${p.name}@${m.name}`;
27
43
  const installed = installedPlugins.plugins.find(
28
44
  (ip) => ip.name === p.name && ip.marketplace === m.name,
29
45
  );
@@ -32,6 +48,7 @@ export async function listPluginsCommand() {
32
48
  marketplace: m.name,
33
49
  installed: !!installed,
34
50
  version: installed?.version,
51
+ scope: pluginScopeManager.findPluginScope(pluginId) || undefined,
35
52
  });
36
53
  });
37
54
  } catch {
@@ -52,7 +69,8 @@ export async function listPluginsCommand() {
52
69
  : "disabled"
53
70
  : "not installed";
54
71
  const versionStr = p.version ? ` v${p.version}` : "";
55
- console.log(`- ${pluginId}${versionStr} [${status}]`);
72
+ const scopeStr = p.scope ? ` (${p.scope})` : "";
73
+ console.log(`- ${pluginId}${versionStr}${scopeStr} [${status}]`);
56
74
  });
57
75
  }
58
76
  process.exit(0);
@@ -42,7 +42,10 @@ export async function listMarketplacesCommand() {
42
42
  } else {
43
43
  sourceInfo = source.url + (source.ref ? `#${source.ref}` : "");
44
44
  }
45
- console.log(`- ${m.name}: ${sourceInfo} (${m.source.source})`);
45
+ const builtinLabel = m.isBuiltin ? " [builtin]" : "";
46
+ console.log(
47
+ `- ${m.name}${builtinLabel}: ${sourceInfo} (${m.source.source})`,
48
+ );
46
49
  });
47
50
  }
48
51
  process.exit(0);
@@ -53,6 +56,19 @@ export async function listMarketplacesCommand() {
53
56
  }
54
57
  }
55
58
 
59
+ export async function removeMarketplaceCommand(argv: { name: string }) {
60
+ const service = new MarketplaceService();
61
+ try {
62
+ await service.removeMarketplace(argv.name);
63
+ console.log(`Successfully removed marketplace: ${argv.name}`);
64
+ process.exit(0);
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ console.error(`Failed to remove marketplace: ${message}`);
68
+ process.exit(1);
69
+ }
70
+ }
71
+
56
72
  export async function updateMarketplaceCommand(argv: { name?: string }) {
57
73
  const service = new MarketplaceService();
58
74
  try {
@@ -0,0 +1,39 @@
1
+ import {
2
+ MarketplaceService,
3
+ ConfigurationService,
4
+ PluginManager,
5
+ PluginScopeManager,
6
+ } from "wave-agent-sdk";
7
+
8
+ export async function uninstallPluginCommand(argv: { plugin: string }) {
9
+ const marketplaceService = new MarketplaceService();
10
+ const workdir = process.cwd();
11
+
12
+ try {
13
+ await marketplaceService.uninstallPlugin(argv.plugin);
14
+ console.log(`Successfully uninstalled plugin: ${argv.plugin}`);
15
+
16
+ const configurationService = new ConfigurationService();
17
+ const pluginManager = new PluginManager({ workdir });
18
+ const scopeManager = new PluginScopeManager({
19
+ workdir,
20
+ configurationService,
21
+ pluginManager,
22
+ });
23
+
24
+ try {
25
+ await scopeManager.removePluginFromAllScopes(argv.plugin);
26
+ console.log(`Cleaned up plugin configuration from all scopes`);
27
+ } catch (error) {
28
+ console.warn(
29
+ `Warning: Could not clean up all plugin configurations: ${error instanceof Error ? error.message : String(error)}`,
30
+ );
31
+ }
32
+
33
+ process.exit(0);
34
+ } catch (error) {
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ console.error(`Failed to uninstall plugin: ${message}`);
37
+ process.exit(1);
38
+ }
39
+ }
@@ -0,0 +1,19 @@
1
+ import { MarketplaceService } from "wave-agent-sdk";
2
+
3
+ export async function updatePluginCommand(argv: { plugin: string }) {
4
+ const marketplaceService = new MarketplaceService();
5
+
6
+ try {
7
+ const updated = await marketplaceService.updatePlugin(argv.plugin);
8
+ console.log(
9
+ `Successfully updated plugin: ${updated.name} v${updated.version} from ${updated.marketplace}`,
10
+ );
11
+ console.log(`Cache path: ${updated.cachePath}`);
12
+
13
+ process.exit(0);
14
+ } catch (error) {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ console.error(`Failed to update plugin: ${message}`);
17
+ process.exit(1);
18
+ }
19
+ }
@@ -27,6 +27,7 @@ export const ChatInterface: React.FC = () => {
27
27
  confirmingTool,
28
28
  handleConfirmationDecision,
29
29
  handleConfirmationCancel,
30
+ rewindId,
30
31
  } = useChat();
31
32
 
32
33
  if (!sessionId) return null;
@@ -40,7 +41,7 @@ export const ChatInterface: React.FC = () => {
40
41
  isCompressing={isCompressing}
41
42
  latestTotalTokens={latestTotalTokens}
42
43
  isExpanded={isExpanded}
43
- key={String(isExpanded) + sessionId}
44
+ key={String(isExpanded) + sessionId + rewindId}
44
45
  />
45
46
 
46
47
  {isConfirmationVisible && (
@@ -15,6 +15,13 @@ const AVAILABLE_COMMANDS: SlashCommand[] = [
15
15
  description: "View and manage MCP servers",
16
16
  handler: () => {}, // Handler here won't be used, actual processing is in the hook
17
17
  },
18
+ {
19
+ id: "rewind",
20
+ name: "rewind",
21
+ description:
22
+ "Revert conversation and file changes to a previous checkpoint",
23
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
24
+ },
18
25
  ];
19
26
 
20
27
  export interface CommandSelectorProps {
@@ -229,7 +229,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
229
229
  } else if (state.selectedOption === "auto") {
230
230
  if (toolName === BASH_TOOL_NAME) {
231
231
  const rule = suggestedPrefix
232
- ? `Bash(${suggestedPrefix}:*)`
232
+ ? `Bash(${suggestedPrefix}*)`
233
233
  : `Bash(${toolInput?.command})`;
234
234
  onDecision({
235
235
  behavior: "allow",