wave-code 0.13.6 → 0.14.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 (80) hide show
  1. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  2. package/dist/components/BackgroundTaskManager.js +36 -25
  3. package/dist/components/ChatInterface.d.ts.map +1 -1
  4. package/dist/components/ChatInterface.js +1 -1
  5. package/dist/components/ConfirmationSelector.d.ts +0 -1
  6. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  7. package/dist/components/ConfirmationSelector.js +111 -259
  8. package/dist/components/InputBox.d.ts.map +1 -1
  9. package/dist/components/InputBox.js +2 -2
  10. package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
  11. package/dist/components/MarketplaceAddForm.js +48 -17
  12. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  13. package/dist/components/MarketplaceDetail.js +2 -1
  14. package/dist/components/MarketplaceList.d.ts.map +1 -1
  15. package/dist/components/MarketplaceList.js +2 -1
  16. package/dist/components/McpManager.d.ts.map +1 -1
  17. package/dist/components/McpManager.js +22 -27
  18. package/dist/components/MessageList.d.ts.map +1 -1
  19. package/dist/components/MessageList.js +4 -6
  20. package/dist/components/PluginDetail.d.ts.map +1 -1
  21. package/dist/components/PluginDetail.js +22 -22
  22. package/dist/components/PluginManagerShell.d.ts +1 -0
  23. package/dist/components/PluginManagerShell.d.ts.map +1 -1
  24. package/dist/components/PluginManagerShell.js +2 -2
  25. package/dist/components/PluginManagerTypes.d.ts +2 -2
  26. package/dist/components/PluginManagerTypes.d.ts.map +1 -1
  27. package/dist/components/TaskNotificationMessage.js +1 -1
  28. package/dist/contexts/useChat.d.ts +1 -0
  29. package/dist/contexts/useChat.d.ts.map +1 -1
  30. package/dist/contexts/useChat.js +128 -102
  31. package/dist/hooks/usePluginManager.d.ts +3 -1
  32. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  33. package/dist/hooks/usePluginManager.js +16 -7
  34. package/dist/managers/inputHandlers.d.ts.map +1 -1
  35. package/dist/managers/inputHandlers.js +6 -9
  36. package/dist/managers/inputReducer.d.ts +6 -0
  37. package/dist/managers/inputReducer.d.ts.map +1 -1
  38. package/dist/managers/inputReducer.js +15 -0
  39. package/dist/reducers/backgroundTaskManagerReducer.d.ts +43 -0
  40. package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -0
  41. package/dist/reducers/backgroundTaskManagerReducer.js +23 -0
  42. package/dist/reducers/confirmationReducer.d.ts +26 -0
  43. package/dist/reducers/confirmationReducer.d.ts.map +1 -0
  44. package/dist/reducers/confirmationReducer.js +45 -0
  45. package/dist/reducers/marketplaceAddFormReducer.d.ts +24 -0
  46. package/dist/reducers/marketplaceAddFormReducer.d.ts.map +1 -0
  47. package/dist/reducers/marketplaceAddFormReducer.js +18 -0
  48. package/dist/reducers/mcpManagerReducer.d.ts +16 -0
  49. package/dist/reducers/mcpManagerReducer.d.ts.map +1 -0
  50. package/dist/reducers/mcpManagerReducer.js +15 -0
  51. package/dist/reducers/pluginDetailReducer.d.ts +25 -0
  52. package/dist/reducers/pluginDetailReducer.d.ts.map +1 -0
  53. package/dist/reducers/pluginDetailReducer.js +38 -0
  54. package/dist/reducers/questionReducer.d.ts +69 -0
  55. package/dist/reducers/questionReducer.d.ts.map +1 -0
  56. package/dist/reducers/questionReducer.js +195 -0
  57. package/package.json +2 -2
  58. package/src/components/BackgroundTaskManager.tsx +32 -34
  59. package/src/components/ChatInterface.tsx +0 -1
  60. package/src/components/ConfirmationSelector.tsx +122 -304
  61. package/src/components/InputBox.tsx +7 -1
  62. package/src/components/MarketplaceAddForm.tsx +76 -22
  63. package/src/components/MarketplaceDetail.tsx +4 -0
  64. package/src/components/MarketplaceList.tsx +4 -0
  65. package/src/components/McpManager.tsx +30 -34
  66. package/src/components/MessageList.tsx +10 -14
  67. package/src/components/PluginDetail.tsx +25 -33
  68. package/src/components/PluginManagerShell.tsx +3 -2
  69. package/src/components/PluginManagerTypes.ts +8 -2
  70. package/src/components/TaskNotificationMessage.tsx +1 -1
  71. package/src/contexts/useChat.tsx +57 -24
  72. package/src/hooks/usePluginManager.ts +18 -7
  73. package/src/managers/inputHandlers.ts +6 -8
  74. package/src/managers/inputReducer.ts +19 -0
  75. package/src/reducers/backgroundTaskManagerReducer.ts +60 -0
  76. package/src/reducers/confirmationReducer.ts +74 -0
  77. package/src/reducers/marketplaceAddFormReducer.ts +35 -0
  78. package/src/reducers/mcpManagerReducer.ts +31 -0
  79. package/src/reducers/pluginDetailReducer.ts +58 -0
  80. package/src/reducers/questionReducer.ts +251 -0
@@ -0,0 +1,16 @@
1
+ export interface McpManagerState {
2
+ selectedIndex: number;
3
+ viewMode: "list" | "detail";
4
+ }
5
+ export type McpManagerAction = {
6
+ type: "MOVE_UP";
7
+ serverCount: number;
8
+ } | {
9
+ type: "MOVE_DOWN";
10
+ serverCount: number;
11
+ } | {
12
+ type: "SET_VIEW_MODE";
13
+ viewMode: "list" | "detail";
14
+ };
15
+ export declare function mcpManagerReducer(state: McpManagerState, action: McpManagerAction): McpManagerState;
16
+ //# sourceMappingURL=mcpManagerReducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpManagerReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/mcpManagerReducer.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,gBAAgB,GACvB,eAAe,CAiBjB"}
@@ -0,0 +1,15 @@
1
+ export function mcpManagerReducer(state, action) {
2
+ switch (action.type) {
3
+ case "MOVE_UP":
4
+ return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
5
+ case "MOVE_DOWN":
6
+ return {
7
+ ...state,
8
+ selectedIndex: Math.min(action.serverCount - 1, state.selectedIndex + 1),
9
+ };
10
+ case "SET_VIEW_MODE":
11
+ return { ...state, viewMode: action.viewMode };
12
+ default:
13
+ return state;
14
+ }
15
+ }
@@ -0,0 +1,25 @@
1
+ export interface PluginDetailState {
2
+ selectedScopeIndex: number;
3
+ selectedActionIndex: number;
4
+ }
5
+ export type PluginDetailAction = {
6
+ type: "SELECT_SCOPE_INDEX";
7
+ index: number;
8
+ } | {
9
+ type: "SELECT_ACTION_INDEX";
10
+ index: number;
11
+ } | {
12
+ type: "MOVE_SCOPE_UP";
13
+ maxIndex: number;
14
+ } | {
15
+ type: "MOVE_SCOPE_DOWN";
16
+ maxIndex: number;
17
+ } | {
18
+ type: "MOVE_ACTION_UP";
19
+ maxIndex: number;
20
+ } | {
21
+ type: "MOVE_ACTION_DOWN";
22
+ maxIndex: number;
23
+ };
24
+ export declare function pluginDetailReducer(state: PluginDetailState, action: PluginDetailAction): PluginDetailState;
25
+ //# sourceMappingURL=pluginDetailReducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginDetailReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/pluginDetailReducer.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAyCnB"}
@@ -0,0 +1,38 @@
1
+ export function pluginDetailReducer(state, action) {
2
+ switch (action.type) {
3
+ case "SELECT_SCOPE_INDEX":
4
+ return { ...state, selectedScopeIndex: action.index };
5
+ case "SELECT_ACTION_INDEX":
6
+ return { ...state, selectedActionIndex: action.index };
7
+ case "MOVE_SCOPE_UP":
8
+ return {
9
+ ...state,
10
+ selectedScopeIndex: state.selectedScopeIndex > 0
11
+ ? state.selectedScopeIndex - 1
12
+ : action.maxIndex,
13
+ };
14
+ case "MOVE_SCOPE_DOWN":
15
+ return {
16
+ ...state,
17
+ selectedScopeIndex: state.selectedScopeIndex < action.maxIndex
18
+ ? state.selectedScopeIndex + 1
19
+ : 0,
20
+ };
21
+ case "MOVE_ACTION_UP":
22
+ return {
23
+ ...state,
24
+ selectedActionIndex: state.selectedActionIndex > 0
25
+ ? state.selectedActionIndex - 1
26
+ : action.maxIndex,
27
+ };
28
+ case "MOVE_ACTION_DOWN":
29
+ return {
30
+ ...state,
31
+ selectedActionIndex: state.selectedActionIndex < action.maxIndex
32
+ ? state.selectedActionIndex + 1
33
+ : 0,
34
+ };
35
+ default:
36
+ return state;
37
+ }
38
+ }
@@ -0,0 +1,69 @@
1
+ import type { PermissionDecision } from "wave-agent-sdk";
2
+ export interface QuestionSavedState {
3
+ selectedOptionIndex: number;
4
+ selectedOptionIndices: Set<number>;
5
+ otherText: string;
6
+ otherCursorPosition: number;
7
+ }
8
+ export interface QuestionState {
9
+ currentQuestionIndex: number;
10
+ selectedOptionIndex: number;
11
+ selectedOptionIndices: Set<number>;
12
+ userAnswers: Record<string, string>;
13
+ otherText: string;
14
+ otherCursorPosition: number;
15
+ savedStates: Record<number, QuestionSavedState>;
16
+ decision: PermissionDecision | null;
17
+ }
18
+ export type QuestionAction = {
19
+ type: "SELECT_OPTION";
20
+ index: number;
21
+ } | {
22
+ type: "MOVE_OPTION_UP";
23
+ maxIndex: number;
24
+ } | {
25
+ type: "MOVE_OPTION_DOWN";
26
+ maxIndex: number;
27
+ } | {
28
+ type: "TOGGLE_MULTI_SELECT";
29
+ optionsLength: number;
30
+ } | {
31
+ type: "CYCLE_QUESTION";
32
+ shift: boolean;
33
+ questionCount: number;
34
+ } | {
35
+ type: "INSERT_OTHER";
36
+ text: string;
37
+ optionsLength: number;
38
+ } | {
39
+ type: "DELETE_OTHER";
40
+ optionsLength: number;
41
+ } | {
42
+ type: "MOVE_OTHER_LEFT";
43
+ optionsLength: number;
44
+ } | {
45
+ type: "MOVE_OTHER_RIGHT";
46
+ optionsLength: number;
47
+ } | {
48
+ type: "CONFIRM_ANSWER";
49
+ currentQuestion: {
50
+ question: string;
51
+ options: Array<{
52
+ label: string;
53
+ }>;
54
+ multiSelect?: boolean;
55
+ };
56
+ options: Array<{
57
+ label: string;
58
+ }>;
59
+ isMultiSelect: boolean;
60
+ questions: Array<{
61
+ question: string;
62
+ options: Array<{
63
+ label: string;
64
+ }>;
65
+ multiSelect?: boolean;
66
+ }>;
67
+ };
68
+ export declare function questionReducer(state: QuestionState, action: QuestionAction): QuestionState;
69
+ //# sourceMappingURL=questionReducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questionReducer.d.ts","sourceRoot":"","sources":["../../src/reducers/questionReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9C;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,GACD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACnD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,eAAe,EAAE;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClC,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClC,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AA+BN,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,cAAc,GACrB,aAAa,CAyKf"}
@@ -0,0 +1,195 @@
1
+ function buildAnswerFromSavedState(q, s) {
2
+ const opts = [...q.options, { label: "Other" }];
3
+ let a = "";
4
+ if (q.multiSelect) {
5
+ const selectedLabels = Array.from(s.selectedOptionIndices)
6
+ .filter((i) => i < q.options.length)
7
+ .map((i) => q.options[i].label);
8
+ const isOtherChecked = s.selectedOptionIndices.has(opts.length - 1);
9
+ if (isOtherChecked && s.otherText.trim()) {
10
+ selectedLabels.push(s.otherText.trim());
11
+ }
12
+ a = selectedLabels.join(", ");
13
+ }
14
+ else {
15
+ if (s.selectedOptionIndex === opts.length - 1) {
16
+ a = s.otherText.trim();
17
+ }
18
+ else {
19
+ a = opts[s.selectedOptionIndex].label;
20
+ }
21
+ }
22
+ return a;
23
+ }
24
+ export function questionReducer(state, action) {
25
+ switch (action.type) {
26
+ case "SELECT_OPTION":
27
+ return { ...state, selectedOptionIndex: action.index };
28
+ case "MOVE_OPTION_UP":
29
+ return {
30
+ ...state,
31
+ selectedOptionIndex: Math.max(0, state.selectedOptionIndex - 1),
32
+ };
33
+ case "MOVE_OPTION_DOWN":
34
+ return {
35
+ ...state,
36
+ selectedOptionIndex: Math.min(action.maxIndex, state.selectedOptionIndex + 1),
37
+ };
38
+ case "TOGGLE_MULTI_SELECT": {
39
+ const nextIndices = new Set(state.selectedOptionIndices);
40
+ if (nextIndices.has(state.selectedOptionIndex))
41
+ nextIndices.delete(state.selectedOptionIndex);
42
+ else
43
+ nextIndices.add(state.selectedOptionIndex);
44
+ return { ...state, selectedOptionIndices: nextIndices };
45
+ }
46
+ case "CYCLE_QUESTION": {
47
+ const direction = action.shift ? -1 : 1;
48
+ let nextIndex = state.currentQuestionIndex + direction;
49
+ if (nextIndex < 0)
50
+ nextIndex = action.questionCount - 1;
51
+ if (nextIndex >= action.questionCount)
52
+ nextIndex = 0;
53
+ if (nextIndex === state.currentQuestionIndex)
54
+ return state;
55
+ const savedStates = {
56
+ ...state.savedStates,
57
+ [state.currentQuestionIndex]: {
58
+ selectedOptionIndex: state.selectedOptionIndex,
59
+ selectedOptionIndices: state.selectedOptionIndices,
60
+ otherText: state.otherText,
61
+ otherCursorPosition: state.otherCursorPosition,
62
+ },
63
+ };
64
+ const nextState = savedStates[nextIndex] || {
65
+ selectedOptionIndex: 0,
66
+ selectedOptionIndices: new Set(),
67
+ otherText: "",
68
+ otherCursorPosition: 0,
69
+ };
70
+ return {
71
+ ...state,
72
+ currentQuestionIndex: nextIndex,
73
+ ...nextState,
74
+ savedStates,
75
+ };
76
+ }
77
+ case "INSERT_OTHER": {
78
+ if (state.selectedOptionIndex !== action.optionsLength - 1)
79
+ return state;
80
+ const newText = state.otherText.slice(0, state.otherCursorPosition) +
81
+ action.text +
82
+ state.otherText.slice(state.otherCursorPosition);
83
+ return {
84
+ ...state,
85
+ otherText: newText,
86
+ otherCursorPosition: state.otherCursorPosition + action.text.length,
87
+ };
88
+ }
89
+ case "DELETE_OTHER":
90
+ if (state.selectedOptionIndex !== action.optionsLength - 1)
91
+ return state;
92
+ if (state.otherCursorPosition > 0) {
93
+ return {
94
+ ...state,
95
+ otherText: state.otherText.slice(0, state.otherCursorPosition - 1) +
96
+ state.otherText.slice(state.otherCursorPosition),
97
+ otherCursorPosition: state.otherCursorPosition - 1,
98
+ };
99
+ }
100
+ return state;
101
+ case "MOVE_OTHER_LEFT":
102
+ if (state.selectedOptionIndex !== action.optionsLength - 1)
103
+ return state;
104
+ return {
105
+ ...state,
106
+ otherCursorPosition: Math.max(0, state.otherCursorPosition - 1),
107
+ };
108
+ case "MOVE_OTHER_RIGHT":
109
+ if (state.selectedOptionIndex !== action.optionsLength - 1)
110
+ return state;
111
+ return {
112
+ ...state,
113
+ otherCursorPosition: Math.min(state.otherText.length, state.otherCursorPosition + 1),
114
+ };
115
+ case "CONFIRM_ANSWER": {
116
+ const { currentQuestion: cq, options, isMultiSelect, questions } = action;
117
+ const isOtherFocused = state.selectedOptionIndex === options.length - 1;
118
+ let answer = "";
119
+ if (isMultiSelect) {
120
+ const selectedLabels = Array.from(state.selectedOptionIndices)
121
+ .filter((i) => i < cq.options.length)
122
+ .map((i) => cq.options[i].label);
123
+ const isOtherChecked = state.selectedOptionIndices.has(options.length - 1);
124
+ if (isOtherChecked && state.otherText.trim()) {
125
+ selectedLabels.push(state.otherText.trim());
126
+ }
127
+ answer = selectedLabels.join(", ");
128
+ }
129
+ else {
130
+ if (isOtherFocused) {
131
+ answer = state.otherText.trim();
132
+ }
133
+ else {
134
+ answer = options[state.selectedOptionIndex].label;
135
+ }
136
+ }
137
+ if (!answer)
138
+ return state;
139
+ const newAnswers = {
140
+ ...state.userAnswers,
141
+ [cq.question]: answer,
142
+ };
143
+ if (state.currentQuestionIndex < questions.length - 1) {
144
+ const nextIndex = state.currentQuestionIndex + 1;
145
+ const savedStates = {
146
+ ...state.savedStates,
147
+ [state.currentQuestionIndex]: {
148
+ selectedOptionIndex: state.selectedOptionIndex,
149
+ selectedOptionIndices: state.selectedOptionIndices,
150
+ otherText: state.otherText,
151
+ otherCursorPosition: state.otherCursorPosition,
152
+ },
153
+ };
154
+ const nextState = savedStates[nextIndex] || {
155
+ selectedOptionIndex: 0,
156
+ selectedOptionIndices: new Set(),
157
+ otherText: "",
158
+ otherCursorPosition: 0,
159
+ };
160
+ return {
161
+ ...state,
162
+ currentQuestionIndex: nextIndex,
163
+ ...nextState,
164
+ userAnswers: newAnswers,
165
+ savedStates,
166
+ };
167
+ }
168
+ else {
169
+ const finalAnswers = { ...newAnswers };
170
+ for (const [idxStr, s] of Object.entries(state.savedStates)) {
171
+ const idx = parseInt(idxStr);
172
+ const q = questions[idx];
173
+ if (q && !finalAnswers[q.question]) {
174
+ const a = buildAnswerFromSavedState(q, s);
175
+ if (a)
176
+ finalAnswers[q.question] = a;
177
+ }
178
+ }
179
+ const allAnswered = questions.every((q) => finalAnswers[q.question]);
180
+ if (!allAnswered)
181
+ return state;
182
+ return {
183
+ ...state,
184
+ userAnswers: finalAnswers,
185
+ decision: {
186
+ behavior: "allow",
187
+ message: JSON.stringify(finalAnswers),
188
+ },
189
+ };
190
+ }
191
+ }
192
+ default:
193
+ return state;
194
+ }
195
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.13.6",
3
+ "version": "0.14.1",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,7 +42,7 @@
42
42
  "semver": "^7.7.4",
43
43
  "yargs": "^17.7.2",
44
44
  "zod": "^3.23.8",
45
- "wave-agent-sdk": "0.13.6"
45
+ "wave-agent-sdk": "0.14.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "^19.1.8",
@@ -1,18 +1,11 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React, { useEffect, useReducer } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import { useChat } from "../contexts/useChat.js";
4
4
  import { getLastLines } from "wave-agent-sdk";
5
-
6
- interface Task {
7
- id: string;
8
- type: string;
9
- description?: string;
10
- status: "running" | "completed" | "failed" | "killed";
11
- startTime: number;
12
- exitCode?: number;
13
- runtime?: number;
14
- outputPath?: string;
15
- }
5
+ import {
6
+ backgroundTaskManagerReducer,
7
+ type BackgroundTaskManagerState,
8
+ } from "../reducers/backgroundTaskManagerReducer.js";
16
9
 
17
10
  export interface BackgroundTaskManagerProps {
18
11
  onCancel: () => void;
@@ -23,22 +16,23 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
23
16
  }) => {
24
17
  const { backgroundTasks, getBackgroundTaskOutput, stopBackgroundTask } =
25
18
  useChat();
26
- const [tasks, setTasks] = useState<Task[]>([]);
27
- const [selectedIndex, setSelectedIndex] = useState(0);
28
19
  const MAX_VISIBLE_ITEMS = 3;
29
- const [viewMode, setViewMode] = useState<"list" | "detail">("list");
30
- const [detailTaskId, setDetailTaskId] = useState<string | null>(null);
31
- const [detailOutput, setDetailOutput] = useState<{
32
- stdout: string;
33
- stderr: string;
34
- status: string;
35
- outputPath?: string;
36
- } | null>(null);
20
+
21
+ const [state, dispatch] = useReducer(backgroundTaskManagerReducer, {
22
+ tasks: [],
23
+ selectedIndex: 0,
24
+ viewMode: "list",
25
+ detailTaskId: null,
26
+ detailOutput: null,
27
+ } as BackgroundTaskManagerState);
28
+
29
+ const { tasks, selectedIndex, viewMode, detailTaskId, detailOutput } = state;
37
30
 
38
31
  // Convert backgroundTasks to local Task format
39
32
  useEffect(() => {
40
- setTasks(
41
- backgroundTasks.map((task) => ({
33
+ dispatch({
34
+ type: "SET_TASKS",
35
+ tasks: backgroundTasks.map((task) => ({
42
36
  id: task.id,
43
37
  type: task.type,
44
38
  description: task.description,
@@ -48,14 +42,14 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
48
42
  runtime: task.runtime,
49
43
  outputPath: task.outputPath,
50
44
  })),
51
- );
45
+ });
52
46
  }, [backgroundTasks]);
53
47
 
54
48
  // Load detail output for selected task
55
49
  useEffect(() => {
56
50
  if (viewMode === "detail" && detailTaskId) {
57
51
  const output = getBackgroundTaskOutput(detailTaskId);
58
- setDetailOutput(output);
52
+ dispatch({ type: "SET_DETAIL_OUTPUT", output });
59
53
  }
60
54
  }, [viewMode, detailTaskId, getBackgroundTaskOutput]);
61
55
 
@@ -91,8 +85,8 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
91
85
  if (key.return) {
92
86
  if (tasks.length > 0 && selectedIndex < tasks.length) {
93
87
  const selectedTask = tasks[selectedIndex];
94
- setDetailTaskId(selectedTask.id);
95
- setViewMode("detail");
88
+ dispatch({ type: "SET_DETAIL_TASK_ID", id: selectedTask.id });
89
+ dispatch({ type: "SET_VIEW_MODE", mode: "detail" });
96
90
  }
97
91
  return;
98
92
  }
@@ -103,12 +97,18 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
103
97
  }
104
98
 
105
99
  if (key.upArrow) {
106
- setSelectedIndex(Math.max(0, selectedIndex - 1));
100
+ dispatch({
101
+ type: "SELECT_INDEX",
102
+ index: Math.max(0, selectedIndex - 1),
103
+ });
107
104
  return;
108
105
  }
109
106
 
110
107
  if (key.downArrow) {
111
- setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
108
+ dispatch({
109
+ type: "SELECT_INDEX",
110
+ index: Math.min(tasks.length - 1, selectedIndex + 1),
111
+ });
112
112
  return;
113
113
  }
114
114
 
@@ -122,9 +122,7 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
122
122
  } else if (viewMode === "detail") {
123
123
  // Detail mode navigation
124
124
  if (key.escape) {
125
- setViewMode("list");
126
- setDetailTaskId(null);
127
- setDetailOutput(null);
125
+ dispatch({ type: "RESET_DETAIL" });
128
126
  return;
129
127
  }
130
128
 
@@ -141,7 +139,7 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
141
139
  if (viewMode === "detail" && detailTaskId && detailOutput) {
142
140
  const task = tasks.find((t) => t.id === detailTaskId);
143
141
  if (!task) {
144
- setViewMode("list");
142
+ dispatch({ type: "RESET_DETAIL" });
145
143
  return null;
146
144
  }
147
145
 
@@ -133,7 +133,6 @@ export const ChatInterface: React.FC = () => {
133
133
  isExpanded={isExpanded}
134
134
  onDecision={handleConfirmationDecision}
135
135
  onCancel={handleConfirmationCancel}
136
- onAbort={abortMessage}
137
136
  />
138
137
  </>
139
138
  )}