wave-code 0.14.4 → 0.15.1

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 (116) hide show
  1. package/dist/acp/agent.d.ts.map +1 -1
  2. package/dist/acp/agent.js +10 -0
  3. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  4. package/dist/components/BackgroundTaskManager.js +17 -53
  5. package/dist/components/CommandSelector.d.ts.map +1 -1
  6. package/dist/components/CommandSelector.js +38 -28
  7. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  8. package/dist/components/ConfirmationSelector.js +18 -196
  9. package/dist/components/DiscoverView.d.ts.map +1 -1
  10. package/dist/components/DiscoverView.js +19 -11
  11. package/dist/components/FileSelector.d.ts.map +1 -1
  12. package/dist/components/FileSelector.js +29 -25
  13. package/dist/components/HelpView.d.ts.map +1 -1
  14. package/dist/components/HelpView.js +24 -28
  15. package/dist/components/HistorySearch.d.ts.map +1 -1
  16. package/dist/components/HistorySearch.js +26 -28
  17. package/dist/components/InputBox.d.ts.map +1 -1
  18. package/dist/components/InputBox.js +14 -6
  19. package/dist/components/InstalledView.d.ts.map +1 -1
  20. package/dist/components/InstalledView.js +19 -11
  21. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  22. package/dist/components/MarketplaceDetail.js +35 -16
  23. package/dist/components/MarketplaceView.d.ts.map +1 -1
  24. package/dist/components/MarketplaceView.js +21 -12
  25. package/dist/components/McpManager.d.ts.map +1 -1
  26. package/dist/components/McpManager.js +27 -46
  27. package/dist/components/ModelSelector.d.ts.map +1 -1
  28. package/dist/components/ModelSelector.js +27 -17
  29. package/dist/components/PluginDetail.d.ts.map +1 -1
  30. package/dist/components/PluginDetail.js +39 -26
  31. package/dist/components/RewindCommand.d.ts.map +1 -1
  32. package/dist/components/RewindCommand.js +27 -21
  33. package/dist/components/SessionSelector.d.ts.map +1 -1
  34. package/dist/components/SessionSelector.js +21 -16
  35. package/dist/components/StatusLine.d.ts.map +1 -1
  36. package/dist/components/StatusLine.js +5 -1
  37. package/dist/components/WorktreeExitPrompt.d.ts.map +1 -1
  38. package/dist/components/WorktreeExitPrompt.js +21 -12
  39. package/dist/contexts/useChat.d.ts +0 -1
  40. package/dist/contexts/useChat.d.ts.map +1 -1
  41. package/dist/contexts/useChat.js +13 -20
  42. package/dist/hooks/useInputManager.d.ts +5 -19
  43. package/dist/hooks/useInputManager.d.ts.map +1 -1
  44. package/dist/hooks/useInputManager.js +222 -79
  45. package/dist/managers/inputHandlers.d.ts +1 -1
  46. package/dist/managers/inputHandlers.d.ts.map +1 -1
  47. package/dist/managers/inputHandlers.js +3 -6
  48. package/dist/managers/inputReducer.d.ts +46 -4
  49. package/dist/managers/inputReducer.d.ts.map +1 -1
  50. package/dist/managers/inputReducer.js +519 -4
  51. package/dist/print-cli.d.ts.map +1 -1
  52. package/dist/print-cli.js +21 -9
  53. package/dist/reducers/backgroundTaskManagerReducer.d.ts +20 -0
  54. package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -1
  55. package/dist/reducers/backgroundTaskManagerReducer.js +83 -0
  56. package/dist/reducers/confirmationReducer.d.ts +11 -0
  57. package/dist/reducers/confirmationReducer.d.ts.map +1 -1
  58. package/dist/reducers/confirmationReducer.js +146 -0
  59. package/dist/reducers/helpSelectorReducer.d.ts +26 -0
  60. package/dist/reducers/helpSelectorReducer.d.ts.map +1 -0
  61. package/dist/reducers/helpSelectorReducer.js +52 -0
  62. package/dist/reducers/mcpManagerReducer.d.ts +22 -0
  63. package/dist/reducers/mcpManagerReducer.d.ts.map +1 -1
  64. package/dist/reducers/mcpManagerReducer.js +57 -0
  65. package/dist/reducers/pluginDetailReducer.d.ts +8 -0
  66. package/dist/reducers/pluginDetailReducer.d.ts.map +1 -1
  67. package/dist/reducers/pluginDetailReducer.js +32 -0
  68. package/dist/reducers/questionReducer.d.ts +21 -0
  69. package/dist/reducers/questionReducer.d.ts.map +1 -1
  70. package/dist/reducers/questionReducer.js +177 -0
  71. package/dist/reducers/rewindSelectorReducer.d.ts +22 -0
  72. package/dist/reducers/rewindSelectorReducer.d.ts.map +1 -0
  73. package/dist/reducers/rewindSelectorReducer.js +37 -0
  74. package/dist/reducers/selectorReducer.d.ts +22 -0
  75. package/dist/reducers/selectorReducer.d.ts.map +1 -0
  76. package/dist/reducers/selectorReducer.js +40 -0
  77. package/dist/utils/inputUtils.d.ts +18 -0
  78. package/dist/utils/inputUtils.d.ts.map +1 -0
  79. package/dist/utils/inputUtils.js +67 -0
  80. package/dist/utils/worktree.d.ts.map +1 -1
  81. package/dist/utils/worktree.js +1 -2
  82. package/package.json +2 -2
  83. package/src/acp/agent.ts +10 -0
  84. package/src/components/BackgroundTaskManager.tsx +19 -58
  85. package/src/components/CommandSelector.tsx +53 -44
  86. package/src/components/ConfirmationSelector.tsx +18 -202
  87. package/src/components/DiscoverView.tsx +21 -11
  88. package/src/components/FileSelector.tsx +40 -35
  89. package/src/components/HelpView.tsx +37 -43
  90. package/src/components/HistorySearch.tsx +29 -36
  91. package/src/components/InputBox.tsx +14 -7
  92. package/src/components/InstalledView.tsx +21 -11
  93. package/src/components/MarketplaceDetail.tsx +48 -24
  94. package/src/components/MarketplaceView.tsx +25 -10
  95. package/src/components/McpManager.tsx +29 -54
  96. package/src/components/ModelSelector.tsx +30 -23
  97. package/src/components/PluginDetail.tsx +50 -27
  98. package/src/components/RewindCommand.tsx +38 -32
  99. package/src/components/SessionSelector.tsx +23 -20
  100. package/src/components/StatusLine.tsx +10 -1
  101. package/src/components/WorktreeExitPrompt.tsx +23 -13
  102. package/src/contexts/useChat.tsx +13 -31
  103. package/src/hooks/useInputManager.ts +273 -141
  104. package/src/managers/inputHandlers.ts +1 -6
  105. package/src/managers/inputReducer.ts +675 -7
  106. package/src/print-cli.ts +20 -11
  107. package/src/reducers/backgroundTaskManagerReducer.ts +110 -1
  108. package/src/reducers/confirmationReducer.ts +173 -1
  109. package/src/reducers/helpSelectorReducer.ts +77 -0
  110. package/src/reducers/mcpManagerReducer.ts +82 -1
  111. package/src/reducers/pluginDetailReducer.ts +42 -1
  112. package/src/reducers/questionReducer.ts +204 -0
  113. package/src/reducers/rewindSelectorReducer.ts +54 -0
  114. package/src/reducers/selectorReducer.ts +57 -0
  115. package/src/utils/inputUtils.ts +94 -0
  116. package/src/utils/worktree.ts +1 -7
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAqBA,OAAO,EACL,KAAK,KAAK,IAAI,QAAQ,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAUzB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAMpC,MAAM,0BAA0B,CAAC;AAElC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAwBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IAkEnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,uBAAuB;IA8LrC,OAAO,CAAC,cAAc;IAwGtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CA+MxB"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAqBA,OAAO,EACL,KAAK,KAAK,IAAI,QAAQ,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAUzB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAMpC,MAAM,0BAA0B,CAAC;AAElC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAwBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IAmEnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,uBAAuB;IA8LrC,OAAO,CAAC,cAAc;IAwGtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CAwNxB"}
package/dist/acp/agent.js CHANGED
@@ -126,6 +126,7 @@ export class WaveAcpAgent {
126
126
  onTasksChange: (tasks) => callbacks.onTasksChange?.(tasks),
127
127
  onPermissionModeChange: (mode) => callbacks.onPermissionModeChange?.(mode),
128
128
  onModelChange: (model) => callbacks.onModelChange?.(model),
129
+ onUserMessageAdded: (params) => callbacks.onUserMessageAdded?.(params),
129
130
  },
130
131
  });
131
132
  agentRef.instance = agent;
@@ -777,6 +778,15 @@ export class WaveAcpAgent {
777
778
  });
778
779
  }
779
780
  },
781
+ onUserMessageAdded: (params) => {
782
+ this.connection.sessionUpdate({
783
+ sessionId: sessionId,
784
+ update: {
785
+ sessionUpdate: "user_message_chunk",
786
+ content: { type: "text", text: params.content },
787
+ },
788
+ });
789
+ },
780
790
  };
781
791
  }
782
792
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BackgroundTaskManager.d.ts","sourceRoot":"","sources":["../../src/components/BackgroundTaskManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CA8UtE,CAAC"}
1
+ {"version":3,"file":"BackgroundTaskManager.d.ts","sourceRoot":"","sources":["../../src/components/BackgroundTaskManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAuStE,CAAC"}
@@ -13,7 +13,23 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
13
13
  viewMode: "list",
14
14
  detailTaskId: null,
15
15
  detailOutput: null,
16
+ pendingEffect: null,
16
17
  });
18
+ // Handle pending effects
19
+ useEffect(() => {
20
+ if (!state.pendingEffect)
21
+ return;
22
+ const effect = state.pendingEffect;
23
+ dispatch({ type: "CLEAR_PENDING_EFFECT" });
24
+ switch (effect.type) {
25
+ case "CANCEL":
26
+ onCancel();
27
+ break;
28
+ case "STOP_TASK":
29
+ stopBackgroundTask(effect.taskId);
30
+ break;
31
+ }
32
+ }, [state.pendingEffect, onCancel, stopBackgroundTask]);
17
33
  const { tasks, selectedIndex, viewMode, detailTaskId, detailOutput } = state;
18
34
  // Convert backgroundTasks to local Task format
19
35
  useEffect(() => {
@@ -50,63 +66,11 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
50
66
  const formatTime = (timestamp) => {
51
67
  return new Date(timestamp).toLocaleTimeString();
52
68
  };
53
- const stopTask = (taskId) => {
54
- stopBackgroundTask(taskId);
55
- };
56
69
  // Calculate visible window
57
70
  const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, tasks.length - MAX_VISIBLE_ITEMS)));
58
71
  const visibleTasks = tasks.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
59
72
  useInput((input, key) => {
60
- if (viewMode === "list") {
61
- // List mode navigation
62
- if (key.return) {
63
- if (tasks.length > 0 && selectedIndex < tasks.length) {
64
- const selectedTask = tasks[selectedIndex];
65
- dispatch({ type: "SET_DETAIL_TASK_ID", id: selectedTask.id });
66
- dispatch({ type: "SET_VIEW_MODE", mode: "detail" });
67
- }
68
- return;
69
- }
70
- if (key.escape) {
71
- onCancel();
72
- return;
73
- }
74
- if (key.upArrow) {
75
- dispatch({
76
- type: "SELECT_INDEX",
77
- index: Math.max(0, selectedIndex - 1),
78
- });
79
- return;
80
- }
81
- if (key.downArrow) {
82
- dispatch({
83
- type: "SELECT_INDEX",
84
- index: Math.min(tasks.length - 1, selectedIndex + 1),
85
- });
86
- return;
87
- }
88
- if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
89
- const selectedTask = tasks[selectedIndex];
90
- if (selectedTask.status === "running") {
91
- stopTask(selectedTask.id);
92
- }
93
- return;
94
- }
95
- }
96
- else if (viewMode === "detail") {
97
- // Detail mode navigation
98
- if (key.escape) {
99
- dispatch({ type: "RESET_DETAIL" });
100
- return;
101
- }
102
- if (input === "k" && detailTaskId) {
103
- const task = tasks.find((t) => t.id === detailTaskId);
104
- if (task && task.status === "running") {
105
- stopTask(detailTaskId);
106
- }
107
- return;
108
- }
109
- }
73
+ dispatch({ type: "HANDLE_KEY", input, key });
110
74
  });
111
75
  if (viewMode === "detail" && detailTaskId && detailOutput) {
112
76
  const task = tasks.find((t) => t.id === detailTaskId);
@@ -1 +1 @@
1
- {"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+I1D,CAAC"}
1
+ {"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAOnD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAoJ1D,CAAC"}
@@ -1,52 +1,62 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import React, { useState } from "react";
2
+ import { useReducer, useEffect } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
4
  import { AVAILABLE_COMMANDS } from "../constants/commands.js";
5
- export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, commands = [], // Default to empty array
6
- }) => {
5
+ import { selectorReducer, } from "../reducers/selectorReducer.js";
6
+ export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, commands = [], }) => {
7
7
  const MAX_VISIBLE_ITEMS = 3;
8
- const [selectedIndex, setSelectedIndex] = useState(0);
8
+ const [state, dispatch] = useReducer(selectorReducer, {
9
+ selectedIndex: 0,
10
+ pendingDecision: null,
11
+ });
12
+ const { selectedIndex, pendingDecision } = state;
9
13
  // Reset selected index when search query changes
10
- React.useEffect(() => {
11
- setSelectedIndex(0);
14
+ useEffect(() => {
15
+ dispatch({ type: "RESET_INDEX" });
12
16
  }, [searchQuery]);
13
17
  // Merge agent commands and local commands
14
18
  const allCommands = [...AVAILABLE_COMMANDS, ...commands];
15
19
  // Filter command list
16
20
  const filteredCommands = allCommands.filter((command) => !searchQuery ||
17
21
  command.id.toLowerCase().includes(searchQuery.toLowerCase()));
18
- // Calculate visible window
19
- const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, filteredCommands.length - MAX_VISIBLE_ITEMS)));
20
- const visibleCommands = filteredCommands.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
21
- useInput((input, key) => {
22
- if (key.return) {
22
+ // Handle decisions from reducer
23
+ useEffect(() => {
24
+ if (!pendingDecision)
25
+ return;
26
+ if (pendingDecision === "select") {
23
27
  if (filteredCommands.length > 0 &&
24
28
  selectedIndex < filteredCommands.length) {
25
- const selectedCommand = filteredCommands[selectedIndex].id;
26
- onSelect(selectedCommand);
29
+ onSelect(filteredCommands[selectedIndex].id);
27
30
  }
28
- return;
29
31
  }
30
- if (key.tab && onInsert) {
32
+ else if (pendingDecision === "insert" && onInsert) {
31
33
  if (filteredCommands.length > 0 &&
32
34
  selectedIndex < filteredCommands.length) {
33
- const selectedCommand = filteredCommands[selectedIndex].id;
34
- onInsert(selectedCommand);
35
+ onInsert(filteredCommands[selectedIndex].id);
35
36
  }
36
- return;
37
37
  }
38
- if (key.escape) {
38
+ else if (pendingDecision === "cancel") {
39
39
  onCancel();
40
- return;
41
- }
42
- if (key.upArrow) {
43
- setSelectedIndex(Math.max(0, selectedIndex - 1));
44
- return;
45
- }
46
- if (key.downArrow) {
47
- setSelectedIndex(Math.min(filteredCommands.length - 1, selectedIndex + 1));
48
- return;
49
40
  }
41
+ dispatch({ type: "CLEAR_DECISION" });
42
+ }, [
43
+ pendingDecision,
44
+ selectedIndex,
45
+ filteredCommands,
46
+ onSelect,
47
+ onInsert,
48
+ onCancel,
49
+ ]);
50
+ // Calculate visible window
51
+ const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, filteredCommands.length - MAX_VISIBLE_ITEMS)));
52
+ const visibleCommands = filteredCommands.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
53
+ useInput((input, key) => {
54
+ dispatch({
55
+ type: "HANDLE_KEY",
56
+ key,
57
+ maxIndex: filteredCommands.length - 1,
58
+ hasInsert: !!onInsert,
59
+ });
50
60
  });
51
61
  if (filteredCommands.length === 0) {
52
62
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", children: ["No commands found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAsB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CA0cpE,CAAC"}
1
+ {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAmB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAqRpE,CAAC"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useReducer } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
4
  import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
5
- import { confirmationReducer, } from "../reducers/confirmationReducer.js";
5
+ import { confirmationReducer } from "../reducers/confirmationReducer.js";
6
6
  import { questionReducer } from "../reducers/questionReducer.js";
7
7
  const getHeaderColor = (header) => {
8
8
  const colors = ["red", "green", "blue", "magenta", "cyan"];
@@ -35,11 +35,13 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
35
35
  useEffect(() => {
36
36
  if (state.decision) {
37
37
  onDecision(state.decision);
38
+ dispatch({ type: "CLEAR_DECISION" });
38
39
  }
39
40
  }, [state.decision, onDecision]);
40
41
  useEffect(() => {
41
42
  if (questionState.decision) {
42
43
  onDecision(questionState.decision);
44
+ questionDispatch({ type: "CLEAR_DECISION" });
43
45
  }
44
46
  }, [questionState.decision, onDecision]);
45
47
  const currentQuestion = questions[questionState.currentQuestionIndex];
@@ -68,204 +70,24 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
68
70
  return;
69
71
  }
70
72
  if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
71
- if (!currentQuestion)
72
- return;
73
- const options = [...currentQuestion.options, { label: "Other" }];
74
- const isMultiSelect = currentQuestion.multiSelect;
75
- if (key.return) {
76
- questionDispatch({
77
- type: "CONFIRM_ANSWER",
78
- currentQuestion,
79
- options,
80
- isMultiSelect: !!isMultiSelect,
81
- questions,
82
- });
83
- return;
84
- }
85
- if (input === " ") {
86
- const isOtherFocused = questionState.selectedOptionIndex === options.length - 1;
87
- if (isMultiSelect &&
88
- (!isOtherFocused ||
89
- !questionState.selectedOptionIndices.has(questionState.selectedOptionIndex))) {
90
- questionDispatch({
91
- type: "TOGGLE_MULTI_SELECT",
92
- optionsLength: options.length,
93
- });
94
- return;
95
- }
96
- // If it's other and focused, we don't return here, allowing the input handler below to handle it
97
- }
98
- if (key.upArrow) {
99
- questionDispatch({
100
- type: "MOVE_OPTION_UP",
101
- maxIndex: options.length - 1,
102
- });
103
- return;
104
- }
105
- if (key.downArrow) {
106
- questionDispatch({
107
- type: "MOVE_OPTION_DOWN",
108
- maxIndex: options.length - 1,
109
- });
110
- return;
111
- }
112
- if (key.tab) {
113
- questionDispatch({
114
- type: "CYCLE_QUESTION",
115
- shift: key.shift,
116
- questionCount: questions.length,
117
- });
118
- return;
119
- }
120
- // Always dispatch Other actions unconditionally; the reducer checks
121
- // isOtherFocused from the latest state, so this works correctly even
122
- // when inputs are batched before React re-renders.
123
- if (key.leftArrow) {
124
- questionDispatch({
125
- type: "MOVE_OTHER_LEFT",
126
- optionsLength: options.length,
127
- });
128
- return;
129
- }
130
- if (key.rightArrow) {
131
- questionDispatch({
132
- type: "MOVE_OTHER_RIGHT",
133
- optionsLength: options.length,
134
- });
135
- return;
136
- }
137
- if (key.backspace || key.delete) {
138
- questionDispatch({
139
- type: "DELETE_OTHER",
140
- optionsLength: options.length,
141
- });
142
- return;
143
- }
144
- if (input && !key.ctrl && !key.meta) {
145
- questionDispatch({
146
- type: "INSERT_OTHER",
147
- text: input,
148
- optionsLength: options.length,
149
- });
150
- return;
151
- }
152
- return;
153
- }
154
- if (key.return) {
155
- let decision = null;
156
- if (state.selectedOption === "clear") {
157
- decision = {
158
- behavior: "allow",
159
- newPermissionMode: "acceptEdits",
160
- clearContext: true,
161
- };
162
- }
163
- else if (state.selectedOption === "allow") {
164
- if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
165
- decision = { behavior: "allow", newPermissionMode: "default" };
166
- }
167
- else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
168
- decision = { behavior: "allow", newPermissionMode: "plan" };
169
- }
170
- else {
171
- decision = { behavior: "allow" };
172
- }
173
- }
174
- else if (state.selectedOption === "auto") {
175
- if (toolName === BASH_TOOL_NAME) {
176
- const command = toolInput?.command || "";
177
- if (command.trim().startsWith("mkdir")) {
178
- decision = { behavior: "allow", newPermissionMode: "acceptEdits" };
179
- }
180
- else {
181
- const rule = suggestedPrefix
182
- ? `Bash(${suggestedPrefix})`
183
- : `Bash(${toolInput?.command})`;
184
- decision = { behavior: "allow", newPermissionRule: rule };
185
- }
186
- }
187
- else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
188
- decision = { behavior: "allow", newPermissionMode: "plan" };
189
- }
190
- else if (toolName.startsWith("mcp__")) {
191
- decision = { behavior: "allow", newPermissionRule: toolName };
192
- }
193
- else {
194
- decision = { behavior: "allow", newPermissionMode: "acceptEdits" };
195
- }
196
- }
197
- else if (state.alternativeText.trim()) {
198
- decision = { behavior: "deny", message: state.alternativeText.trim() };
199
- }
200
- else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
201
- decision = {
202
- behavior: "deny",
203
- message: "User chose not to enter plan mode",
204
- };
205
- }
206
- if (decision) {
207
- dispatch({ type: "CONFIRM", decision });
208
- }
209
- return;
210
- }
211
- if (state.selectedOption === "alternative") {
212
- if (key.leftArrow) {
213
- dispatch({ type: "MOVE_CURSOR_LEFT" });
214
- return;
215
- }
216
- if (key.rightArrow) {
217
- dispatch({ type: "MOVE_CURSOR_RIGHT" });
218
- return;
219
- }
220
- }
221
- const availableOptions = [];
222
- if (toolName === EXIT_PLAN_MODE_TOOL_NAME)
223
- availableOptions.push("clear");
224
- availableOptions.push("allow");
225
- if (!hidePersistentOption)
226
- availableOptions.push("auto");
227
- availableOptions.push("alternative");
228
- if (key.upArrow) {
229
- const currentIndex = availableOptions.indexOf(state.selectedOption);
230
- if (currentIndex > 0) {
231
- dispatch({
232
- type: "SELECT_OPTION",
233
- option: availableOptions[currentIndex - 1],
234
- });
235
- }
236
- return;
237
- }
238
- if (key.downArrow) {
239
- const currentIndex = availableOptions.indexOf(state.selectedOption);
240
- if (currentIndex < availableOptions.length - 1) {
241
- dispatch({
242
- type: "SELECT_OPTION",
243
- option: availableOptions[currentIndex + 1],
244
- });
245
- }
246
- return;
73
+ questionDispatch({
74
+ type: "HANDLE_KEY",
75
+ input,
76
+ key,
77
+ currentQuestion,
78
+ questions,
79
+ });
247
80
  }
248
- if (key.tab) {
249
- const currentIndex = availableOptions.indexOf(state.selectedOption);
250
- const direction = key.shift ? -1 : 1;
251
- let nextIndex = currentIndex + direction;
252
- if (nextIndex < 0)
253
- nextIndex = availableOptions.length - 1;
254
- if (nextIndex >= availableOptions.length)
255
- nextIndex = 0;
81
+ else {
256
82
  dispatch({
257
- type: "SELECT_OPTION",
258
- option: availableOptions[nextIndex],
83
+ type: "HANDLE_KEY",
84
+ input,
85
+ key,
86
+ toolName,
87
+ toolInput,
88
+ suggestedPrefix,
89
+ hidePersistentOption,
259
90
  });
260
- return;
261
- }
262
- if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
263
- dispatch({ type: "INSERT_TEXT", text: input });
264
- return;
265
- }
266
- if (key.backspace || key.delete) {
267
- dispatch({ type: "BACKSPACE" });
268
- return;
269
91
  }
270
92
  });
271
93
  const placeholderText = "Type here to tell Wave what to change";
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoverView.d.ts","sourceRoot":"","sources":["../../src/components/DiscoverView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAKxC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAyBhC,CAAC"}
1
+ {"version":3,"file":"DiscoverView.d.ts","sourceRoot":"","sources":["../../src/components/DiscoverView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAMrD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAkChC,CAAC"}
@@ -1,25 +1,33 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState } from "react";
2
+ import { useReducer, useEffect } from "react";
3
3
  import { Box, useInput } from "ink";
4
4
  import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
5
5
  import { PluginList } from "./PluginList.js";
6
+ import { selectorReducer } from "../reducers/selectorReducer.js";
6
7
  export const DiscoverView = () => {
7
8
  const { discoverablePlugins, actions } = usePluginManagerContext();
8
- const [selectedIndex, setSelectedIndex] = useState(0);
9
- useInput((input, key) => {
10
- if (key.upArrow) {
11
- setSelectedIndex(Math.max(0, selectedIndex - 1));
12
- }
13
- else if (key.downArrow) {
14
- setSelectedIndex(Math.min(discoverablePlugins.length - 1, selectedIndex + 1));
15
- }
16
- else if (key.return) {
9
+ const [state, dispatch] = useReducer(selectorReducer, {
10
+ selectedIndex: 0,
11
+ pendingDecision: null,
12
+ });
13
+ const { selectedIndex, pendingDecision } = state;
14
+ useInput((_input, key) => {
15
+ dispatch({
16
+ type: "HANDLE_KEY",
17
+ key,
18
+ maxIndex: discoverablePlugins.length - 1,
19
+ hasInsert: false,
20
+ });
21
+ });
22
+ useEffect(() => {
23
+ if (pendingDecision === "select") {
17
24
  const plugin = discoverablePlugins[selectedIndex];
18
25
  if (plugin) {
19
26
  actions.setSelectedId(`${plugin.name}@${plugin.marketplace}`);
20
27
  actions.setView("PLUGIN_DETAIL");
21
28
  }
29
+ dispatch({ type: "CLEAR_DECISION" });
22
30
  }
23
- });
31
+ }, [pendingDecision, selectedIndex, discoverablePlugins, actions]);
24
32
  return (_jsx(Box, { flexDirection: "column", children: _jsx(PluginList, { plugins: discoverablePlugins, selectedIndex: selectedIndex }) }));
25
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAuHpD,CAAC"}
1
+ {"version":3,"file":"FileSelector.d.ts","sourceRoot":"","sources":["../../src/components/FileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAwHpD,CAAC"}
@@ -1,43 +1,47 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from "react";
2
+ import { useReducer, useEffect } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
+ import { selectorReducer, } from "../reducers/selectorReducer.js";
4
5
  export const FileSelector = ({ files, searchQuery, isLoading = false, onSelect, onCancel, }) => {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- useInput((input, key) => {
7
- if (key.return || key.tab) {
6
+ const [state, dispatch] = useReducer(selectorReducer, {
7
+ selectedIndex: 0,
8
+ pendingDecision: null,
9
+ });
10
+ const { selectedIndex, pendingDecision } = state;
11
+ // Reset selected index when files change
12
+ useEffect(() => {
13
+ dispatch({ type: "RESET_INDEX" });
14
+ }, [files]);
15
+ // Handle decisions from reducer
16
+ useEffect(() => {
17
+ if (!pendingDecision)
18
+ return;
19
+ if (pendingDecision === "select" || pendingDecision === "insert") {
8
20
  if (files.length > 0 && selectedIndex < files.length) {
9
21
  onSelect(files[selectedIndex].path);
10
22
  }
11
- return;
12
23
  }
13
- if (key.escape) {
24
+ else if (pendingDecision === "cancel") {
14
25
  onCancel();
15
- return;
16
- }
17
- if (key.upArrow) {
18
- setSelectedIndex(Math.max(0, selectedIndex - 1));
19
- return;
20
- }
21
- if (key.downArrow) {
22
- setSelectedIndex(Math.min(files.length - 1, selectedIndex + 1));
23
- return;
24
26
  }
27
+ dispatch({ type: "CLEAR_DECISION" });
28
+ }, [pendingDecision, selectedIndex, files, onSelect, onCancel]);
29
+ useInput((input, key) => {
30
+ dispatch({
31
+ type: "HANDLE_KEY",
32
+ key,
33
+ maxIndex: files.length - 1,
34
+ hasInsert: true, // For FileSelector, Tab is also select
35
+ });
25
36
  });
26
37
  if (files.length === 0) {
27
38
  return (_jsx(Box, { flexDirection: "column", borderStyle: "single", borderColor: isLoading ? "cyan" : "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: isLoading ? (_jsx(Text, { color: "cyan", bold: true, children: "Select File/Directory..." })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "yellow", children: ["No files found for \"", searchQuery, "\""] }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] })) }));
28
39
  }
29
40
  const maxDisplay = 5;
30
41
  // Calculate display window start and end positions
31
- const getDisplayWindow = () => {
32
- const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxDisplay / 2), Math.max(0, files.length - maxDisplay)));
33
- const endIndex = Math.min(files.length, startIndex + maxDisplay);
34
- return {
35
- startIndex,
36
- endIndex,
37
- displayFiles: files.slice(startIndex, endIndex),
38
- };
39
- };
40
- const { startIndex, displayFiles } = getDisplayWindow();
42
+ const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxDisplay / 2), Math.max(0, files.length - maxDisplay)));
43
+ const endIndex = Math.min(files.length, startIndex + maxDisplay);
44
+ const displayFiles = files.slice(startIndex, endIndex);
41
45
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "cyan", bold: true, children: ["Select File/Directory ", searchQuery && `(filtering: "${searchQuery}")`] }) }), _jsx(Box, { flexDirection: "column", children: displayFiles.map((fileItem, displayIndex) => {
42
46
  const actualIndex = startIndex + displayIndex;
43
47
  const isSelected = actualIndex === selectedIndex;
@@ -1 +1 @@
1
- {"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAqK5C,CAAC"}
1
+ {"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA8J5C,CAAC"}