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.
- package/dist/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +10 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +17 -53
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +38 -28
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +18 -196
- package/dist/components/DiscoverView.d.ts.map +1 -1
- package/dist/components/DiscoverView.js +19 -11
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +29 -25
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +24 -28
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +26 -28
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +14 -6
- package/dist/components/InstalledView.d.ts.map +1 -1
- package/dist/components/InstalledView.js +19 -11
- package/dist/components/MarketplaceDetail.d.ts.map +1 -1
- package/dist/components/MarketplaceDetail.js +35 -16
- package/dist/components/MarketplaceView.d.ts.map +1 -1
- package/dist/components/MarketplaceView.js +21 -12
- package/dist/components/McpManager.d.ts.map +1 -1
- package/dist/components/McpManager.js +27 -46
- package/dist/components/ModelSelector.d.ts.map +1 -1
- package/dist/components/ModelSelector.js +27 -17
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +39 -26
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +27 -21
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +21 -16
- package/dist/components/StatusLine.d.ts.map +1 -1
- package/dist/components/StatusLine.js +5 -1
- package/dist/components/WorktreeExitPrompt.d.ts.map +1 -1
- package/dist/components/WorktreeExitPrompt.js +21 -12
- package/dist/contexts/useChat.d.ts +0 -1
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +13 -20
- package/dist/hooks/useInputManager.d.ts +5 -19
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +222 -79
- package/dist/managers/inputHandlers.d.ts +1 -1
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +3 -6
- package/dist/managers/inputReducer.d.ts +46 -4
- package/dist/managers/inputReducer.d.ts.map +1 -1
- package/dist/managers/inputReducer.js +519 -4
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +21 -9
- package/dist/reducers/backgroundTaskManagerReducer.d.ts +20 -0
- package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -1
- package/dist/reducers/backgroundTaskManagerReducer.js +83 -0
- package/dist/reducers/confirmationReducer.d.ts +11 -0
- package/dist/reducers/confirmationReducer.d.ts.map +1 -1
- package/dist/reducers/confirmationReducer.js +146 -0
- package/dist/reducers/helpSelectorReducer.d.ts +26 -0
- package/dist/reducers/helpSelectorReducer.d.ts.map +1 -0
- package/dist/reducers/helpSelectorReducer.js +52 -0
- package/dist/reducers/mcpManagerReducer.d.ts +22 -0
- package/dist/reducers/mcpManagerReducer.d.ts.map +1 -1
- package/dist/reducers/mcpManagerReducer.js +57 -0
- package/dist/reducers/pluginDetailReducer.d.ts +8 -0
- package/dist/reducers/pluginDetailReducer.d.ts.map +1 -1
- package/dist/reducers/pluginDetailReducer.js +32 -0
- package/dist/reducers/questionReducer.d.ts +21 -0
- package/dist/reducers/questionReducer.d.ts.map +1 -1
- package/dist/reducers/questionReducer.js +177 -0
- package/dist/reducers/rewindSelectorReducer.d.ts +22 -0
- package/dist/reducers/rewindSelectorReducer.d.ts.map +1 -0
- package/dist/reducers/rewindSelectorReducer.js +37 -0
- package/dist/reducers/selectorReducer.d.ts +22 -0
- package/dist/reducers/selectorReducer.d.ts.map +1 -0
- package/dist/reducers/selectorReducer.js +40 -0
- package/dist/utils/inputUtils.d.ts +18 -0
- package/dist/utils/inputUtils.d.ts.map +1 -0
- package/dist/utils/inputUtils.js +67 -0
- package/dist/utils/worktree.d.ts.map +1 -1
- package/dist/utils/worktree.js +1 -2
- package/package.json +2 -2
- package/src/acp/agent.ts +10 -0
- package/src/components/BackgroundTaskManager.tsx +19 -58
- package/src/components/CommandSelector.tsx +53 -44
- package/src/components/ConfirmationSelector.tsx +18 -202
- package/src/components/DiscoverView.tsx +21 -11
- package/src/components/FileSelector.tsx +40 -35
- package/src/components/HelpView.tsx +37 -43
- package/src/components/HistorySearch.tsx +29 -36
- package/src/components/InputBox.tsx +14 -7
- package/src/components/InstalledView.tsx +21 -11
- package/src/components/MarketplaceDetail.tsx +48 -24
- package/src/components/MarketplaceView.tsx +25 -10
- package/src/components/McpManager.tsx +29 -54
- package/src/components/ModelSelector.tsx +30 -23
- package/src/components/PluginDetail.tsx +50 -27
- package/src/components/RewindCommand.tsx +38 -32
- package/src/components/SessionSelector.tsx +23 -20
- package/src/components/StatusLine.tsx +10 -1
- package/src/components/WorktreeExitPrompt.tsx +23 -13
- package/src/contexts/useChat.tsx +13 -31
- package/src/hooks/useInputManager.ts +273 -141
- package/src/managers/inputHandlers.ts +1 -6
- package/src/managers/inputReducer.ts +675 -7
- package/src/print-cli.ts +20 -11
- package/src/reducers/backgroundTaskManagerReducer.ts +110 -1
- package/src/reducers/confirmationReducer.ts +173 -1
- package/src/reducers/helpSelectorReducer.ts +77 -0
- package/src/reducers/mcpManagerReducer.ts +82 -1
- package/src/reducers/pluginDetailReducer.ts +42 -1
- package/src/reducers/questionReducer.ts +204 -0
- package/src/reducers/rewindSelectorReducer.ts +54 -0
- package/src/reducers/selectorReducer.ts +57 -0
- package/src/utils/inputUtils.ts +94 -0
- package/src/utils/worktree.ts +1 -7
package/src/print-cli.ts
CHANGED
|
@@ -71,18 +71,21 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
71
71
|
isReasoning = false;
|
|
72
72
|
isContent = false;
|
|
73
73
|
// Assistant message started - no content to output yet
|
|
74
|
-
process.stdout.write("\n");
|
|
75
74
|
},
|
|
76
75
|
onAssistantReasoningUpdated: (chunk: string) => {
|
|
77
76
|
if (!isReasoning) {
|
|
78
|
-
process.stdout.write("💭 Reasoning:\n");
|
|
77
|
+
process.stdout.write("\n💭 Reasoning:\n");
|
|
79
78
|
isReasoning = true;
|
|
80
79
|
}
|
|
81
80
|
process.stdout.write(chunk);
|
|
82
81
|
},
|
|
83
82
|
onAssistantContentUpdated: (chunk: string) => {
|
|
84
|
-
if (
|
|
85
|
-
|
|
83
|
+
if (!isContent) {
|
|
84
|
+
if (isReasoning) {
|
|
85
|
+
process.stdout.write("\n\n📝 Response:\n");
|
|
86
|
+
} else {
|
|
87
|
+
process.stdout.write("\n");
|
|
88
|
+
}
|
|
86
89
|
isContent = true;
|
|
87
90
|
}
|
|
88
91
|
// FR-001: Stream content updates for real-time display - output only the new chunk
|
|
@@ -93,24 +96,24 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
93
96
|
onSubagentAssistantMessageAdded: (subagentId: string) => {
|
|
94
97
|
subagentReasoningStates.set(subagentId, false);
|
|
95
98
|
subagentContentStates.set(subagentId, false);
|
|
96
|
-
process.stdout.write("\n ");
|
|
97
99
|
},
|
|
98
100
|
onSubagentAssistantReasoningUpdated: (
|
|
99
101
|
subagentId: string,
|
|
100
102
|
chunk: string,
|
|
101
103
|
) => {
|
|
102
104
|
if (!subagentReasoningStates.get(subagentId)) {
|
|
103
|
-
process.stdout.write("💭 Reasoning: ");
|
|
105
|
+
process.stdout.write("\n 💭 Reasoning: ");
|
|
104
106
|
subagentReasoningStates.set(subagentId, true);
|
|
105
107
|
}
|
|
106
108
|
process.stdout.write(chunk);
|
|
107
109
|
},
|
|
108
110
|
onSubagentAssistantContentUpdated: (subagentId: string, chunk: string) => {
|
|
109
|
-
if (
|
|
110
|
-
subagentReasoningStates.get(subagentId)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
if (!subagentContentStates.get(subagentId)) {
|
|
112
|
+
if (subagentReasoningStates.get(subagentId)) {
|
|
113
|
+
process.stdout.write("\n 📝 Response: ");
|
|
114
|
+
} else {
|
|
115
|
+
process.stdout.write("\n ");
|
|
116
|
+
}
|
|
114
117
|
subagentContentStates.set(subagentId, true);
|
|
115
118
|
}
|
|
116
119
|
process.stdout.write(chunk);
|
|
@@ -161,6 +164,12 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
161
164
|
// Send message if provided and not empty
|
|
162
165
|
if (typeof message === "string" && message.trim() !== "") {
|
|
163
166
|
await agent.sendMessage(message);
|
|
167
|
+
process.stdout.write("\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Wait for running background tasks and subagents to complete
|
|
171
|
+
while (agent.hasRunningBackgroundWork) {
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
// Display usage summary before exit
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
|
|
1
3
|
export interface Task {
|
|
2
4
|
id: string;
|
|
3
5
|
type: string;
|
|
@@ -16,21 +18,31 @@ export interface DetailOutput {
|
|
|
16
18
|
outputPath?: string;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
export type PendingEffect =
|
|
22
|
+
| { type: "CANCEL" }
|
|
23
|
+
| { type: "STOP_TASK"; taskId: string };
|
|
24
|
+
|
|
19
25
|
export interface BackgroundTaskManagerState {
|
|
20
26
|
tasks: Task[];
|
|
21
27
|
selectedIndex: number;
|
|
22
28
|
viewMode: "list" | "detail";
|
|
23
29
|
detailTaskId: string | null;
|
|
24
30
|
detailOutput: DetailOutput | null;
|
|
31
|
+
pendingEffect: PendingEffect | null;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
export type BackgroundTaskManagerAction =
|
|
28
35
|
| { type: "SET_TASKS"; tasks: Task[] }
|
|
29
36
|
| { type: "SELECT_INDEX"; index: number }
|
|
37
|
+
| { type: "MOVE_UP" }
|
|
38
|
+
| { type: "MOVE_DOWN" }
|
|
39
|
+
| { type: "SELECT_CURRENT" }
|
|
30
40
|
| { type: "SET_VIEW_MODE"; mode: "list" | "detail" }
|
|
31
41
|
| { type: "SET_DETAIL_TASK_ID"; id: string | null }
|
|
32
42
|
| { type: "SET_DETAIL_OUTPUT"; output: DetailOutput | null }
|
|
33
|
-
| { type: "RESET_DETAIL" }
|
|
43
|
+
| { type: "RESET_DETAIL" }
|
|
44
|
+
| { type: "HANDLE_KEY"; input: string; key: Key }
|
|
45
|
+
| { type: "CLEAR_PENDING_EFFECT" };
|
|
34
46
|
|
|
35
47
|
export function backgroundTaskManagerReducer(
|
|
36
48
|
state: BackgroundTaskManagerState,
|
|
@@ -41,6 +53,25 @@ export function backgroundTaskManagerReducer(
|
|
|
41
53
|
return { ...state, tasks: action.tasks };
|
|
42
54
|
case "SELECT_INDEX":
|
|
43
55
|
return { ...state, selectedIndex: action.index };
|
|
56
|
+
case "MOVE_UP":
|
|
57
|
+
return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
|
|
58
|
+
case "MOVE_DOWN":
|
|
59
|
+
return {
|
|
60
|
+
...state,
|
|
61
|
+
selectedIndex: Math.min(
|
|
62
|
+
state.tasks.length > 0 ? state.tasks.length - 1 : 0,
|
|
63
|
+
state.selectedIndex + 1,
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
case "SELECT_CURRENT":
|
|
67
|
+
if (state.tasks.length > 0 && state.selectedIndex < state.tasks.length) {
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
detailTaskId: state.tasks[state.selectedIndex].id,
|
|
71
|
+
viewMode: "detail",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return state;
|
|
44
75
|
case "SET_VIEW_MODE":
|
|
45
76
|
return { ...state, viewMode: action.mode };
|
|
46
77
|
case "SET_DETAIL_TASK_ID":
|
|
@@ -54,6 +85,84 @@ export function backgroundTaskManagerReducer(
|
|
|
54
85
|
detailTaskId: null,
|
|
55
86
|
detailOutput: null,
|
|
56
87
|
};
|
|
88
|
+
case "HANDLE_KEY": {
|
|
89
|
+
const { input, key } = action;
|
|
90
|
+
if (state.viewMode === "list") {
|
|
91
|
+
if (key.return) {
|
|
92
|
+
if (
|
|
93
|
+
state.tasks.length > 0 &&
|
|
94
|
+
state.selectedIndex < state.tasks.length
|
|
95
|
+
) {
|
|
96
|
+
return {
|
|
97
|
+
...state,
|
|
98
|
+
detailTaskId: state.tasks[state.selectedIndex].id,
|
|
99
|
+
viewMode: "detail",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return state;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (key.escape) {
|
|
106
|
+
return { ...state, pendingEffect: { type: "CANCEL" } };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (key.upArrow) {
|
|
110
|
+
return {
|
|
111
|
+
...state,
|
|
112
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (key.downArrow) {
|
|
117
|
+
return {
|
|
118
|
+
...state,
|
|
119
|
+
selectedIndex: Math.min(
|
|
120
|
+
state.tasks.length > 0 ? state.tasks.length - 1 : 0,
|
|
121
|
+
state.selectedIndex + 1,
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (input === "k") {
|
|
127
|
+
if (
|
|
128
|
+
state.tasks.length > 0 &&
|
|
129
|
+
state.selectedIndex < state.tasks.length
|
|
130
|
+
) {
|
|
131
|
+
const selectedTask = state.tasks[state.selectedIndex];
|
|
132
|
+
if (selectedTask.status === "running") {
|
|
133
|
+
return {
|
|
134
|
+
...state,
|
|
135
|
+
pendingEffect: { type: "STOP_TASK", taskId: selectedTask.id },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return state;
|
|
140
|
+
}
|
|
141
|
+
} else if (state.viewMode === "detail") {
|
|
142
|
+
if (key.escape) {
|
|
143
|
+
return {
|
|
144
|
+
...state,
|
|
145
|
+
viewMode: "list",
|
|
146
|
+
detailTaskId: null,
|
|
147
|
+
detailOutput: null,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (input === "k" && state.detailTaskId) {
|
|
152
|
+
const task = state.tasks.find((t) => t.id === state.detailTaskId);
|
|
153
|
+
if (task && task.status === "running") {
|
|
154
|
+
return {
|
|
155
|
+
...state,
|
|
156
|
+
pendingEffect: { type: "STOP_TASK", taskId: state.detailTaskId },
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return state;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
case "CLEAR_PENDING_EFFECT":
|
|
165
|
+
return { ...state, pendingEffect: null };
|
|
57
166
|
default:
|
|
58
167
|
return state;
|
|
59
168
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
1
2
|
import type { PermissionDecision } from "wave-agent-sdk";
|
|
3
|
+
import {
|
|
4
|
+
BASH_TOOL_NAME,
|
|
5
|
+
EXIT_PLAN_MODE_TOOL_NAME,
|
|
6
|
+
ENTER_PLAN_MODE_TOOL_NAME,
|
|
7
|
+
} from "wave-agent-sdk";
|
|
2
8
|
|
|
3
9
|
export interface ConfirmationState {
|
|
4
10
|
selectedOption: "clear" | "auto" | "allow" | "alternative";
|
|
@@ -14,7 +20,17 @@ export type ConfirmationAction =
|
|
|
14
20
|
| { type: "BACKSPACE" }
|
|
15
21
|
| { type: "MOVE_CURSOR_LEFT" }
|
|
16
22
|
| { type: "MOVE_CURSOR_RIGHT" }
|
|
17
|
-
| { type: "CONFIRM"; decision: PermissionDecision }
|
|
23
|
+
| { type: "CONFIRM"; decision: PermissionDecision }
|
|
24
|
+
| { type: "CLEAR_DECISION" }
|
|
25
|
+
| {
|
|
26
|
+
type: "HANDLE_KEY";
|
|
27
|
+
input: string;
|
|
28
|
+
key: Key;
|
|
29
|
+
toolName: string;
|
|
30
|
+
toolInput?: Record<string, unknown>;
|
|
31
|
+
suggestedPrefix?: string;
|
|
32
|
+
hidePersistentOption?: boolean;
|
|
33
|
+
};
|
|
18
34
|
|
|
19
35
|
export function confirmationReducer(
|
|
20
36
|
state: ConfirmationState,
|
|
@@ -68,6 +84,162 @@ export function confirmationReducer(
|
|
|
68
84
|
};
|
|
69
85
|
case "CONFIRM":
|
|
70
86
|
return { ...state, decision: action.decision };
|
|
87
|
+
case "CLEAR_DECISION":
|
|
88
|
+
return { ...state, decision: null };
|
|
89
|
+
case "HANDLE_KEY": {
|
|
90
|
+
const {
|
|
91
|
+
input,
|
|
92
|
+
key,
|
|
93
|
+
toolName,
|
|
94
|
+
toolInput,
|
|
95
|
+
suggestedPrefix,
|
|
96
|
+
hidePersistentOption,
|
|
97
|
+
} = action;
|
|
98
|
+
|
|
99
|
+
if (key.return) {
|
|
100
|
+
let decision: PermissionDecision | null = null;
|
|
101
|
+
if (state.selectedOption === "clear") {
|
|
102
|
+
decision = {
|
|
103
|
+
behavior: "allow",
|
|
104
|
+
newPermissionMode: "acceptEdits",
|
|
105
|
+
clearContext: true,
|
|
106
|
+
};
|
|
107
|
+
} else if (state.selectedOption === "allow") {
|
|
108
|
+
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
109
|
+
decision = { behavior: "allow", newPermissionMode: "default" };
|
|
110
|
+
} else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
|
|
111
|
+
decision = { behavior: "allow", newPermissionMode: "plan" };
|
|
112
|
+
} else {
|
|
113
|
+
decision = { behavior: "allow" };
|
|
114
|
+
}
|
|
115
|
+
} else if (state.selectedOption === "auto") {
|
|
116
|
+
if (toolName === BASH_TOOL_NAME) {
|
|
117
|
+
const command = (toolInput?.command as string) || "";
|
|
118
|
+
if (command.trim().startsWith("mkdir")) {
|
|
119
|
+
decision = {
|
|
120
|
+
behavior: "allow",
|
|
121
|
+
newPermissionMode: "acceptEdits",
|
|
122
|
+
};
|
|
123
|
+
} else {
|
|
124
|
+
const rule = suggestedPrefix
|
|
125
|
+
? `Bash(${suggestedPrefix})`
|
|
126
|
+
: `Bash(${toolInput?.command})`;
|
|
127
|
+
decision = { behavior: "allow", newPermissionRule: rule };
|
|
128
|
+
}
|
|
129
|
+
} else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
|
|
130
|
+
decision = { behavior: "allow", newPermissionMode: "plan" };
|
|
131
|
+
} else if (toolName.startsWith("mcp__")) {
|
|
132
|
+
decision = { behavior: "allow", newPermissionRule: toolName };
|
|
133
|
+
} else {
|
|
134
|
+
decision = { behavior: "allow", newPermissionMode: "acceptEdits" };
|
|
135
|
+
}
|
|
136
|
+
} else if (state.alternativeText.trim()) {
|
|
137
|
+
decision = {
|
|
138
|
+
behavior: "deny",
|
|
139
|
+
message: state.alternativeText.trim(),
|
|
140
|
+
};
|
|
141
|
+
} else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
|
|
142
|
+
decision = {
|
|
143
|
+
behavior: "deny",
|
|
144
|
+
message: "User chose not to enter plan mode",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (decision) {
|
|
149
|
+
return { ...state, decision };
|
|
150
|
+
}
|
|
151
|
+
return state;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (state.selectedOption === "alternative") {
|
|
155
|
+
if (key.leftArrow) {
|
|
156
|
+
return {
|
|
157
|
+
...state,
|
|
158
|
+
alternativeCursorPosition: Math.max(
|
|
159
|
+
0,
|
|
160
|
+
state.alternativeCursorPosition - 1,
|
|
161
|
+
),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (key.rightArrow) {
|
|
165
|
+
return {
|
|
166
|
+
...state,
|
|
167
|
+
alternativeCursorPosition: Math.min(
|
|
168
|
+
state.alternativeText.length,
|
|
169
|
+
state.alternativeCursorPosition + 1,
|
|
170
|
+
),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const availableOptions: ConfirmationState["selectedOption"][] = [];
|
|
176
|
+
if (toolName === EXIT_PLAN_MODE_TOOL_NAME) availableOptions.push("clear");
|
|
177
|
+
availableOptions.push("allow");
|
|
178
|
+
if (!hidePersistentOption) availableOptions.push("auto");
|
|
179
|
+
availableOptions.push("alternative");
|
|
180
|
+
|
|
181
|
+
if (key.upArrow) {
|
|
182
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
183
|
+
if (currentIndex > 0) {
|
|
184
|
+
return {
|
|
185
|
+
...state,
|
|
186
|
+
selectedOption: availableOptions[currentIndex - 1],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return state;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (key.downArrow) {
|
|
193
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
194
|
+
if (currentIndex < availableOptions.length - 1) {
|
|
195
|
+
return {
|
|
196
|
+
...state,
|
|
197
|
+
selectedOption: availableOptions[currentIndex + 1],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return state;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (key.tab) {
|
|
204
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
205
|
+
const direction = key.shift ? -1 : 1;
|
|
206
|
+
let nextIndex = currentIndex + direction;
|
|
207
|
+
if (nextIndex < 0) nextIndex = availableOptions.length - 1;
|
|
208
|
+
if (nextIndex >= availableOptions.length) nextIndex = 0;
|
|
209
|
+
return { ...state, selectedOption: availableOptions[nextIndex] };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
213
|
+
const nextText =
|
|
214
|
+
state.alternativeText.slice(0, state.alternativeCursorPosition) +
|
|
215
|
+
input +
|
|
216
|
+
state.alternativeText.slice(state.alternativeCursorPosition);
|
|
217
|
+
return {
|
|
218
|
+
...state,
|
|
219
|
+
selectedOption: "alternative",
|
|
220
|
+
alternativeText: nextText,
|
|
221
|
+
alternativeCursorPosition:
|
|
222
|
+
state.alternativeCursorPosition + input.length,
|
|
223
|
+
hasUserInput: true,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (key.backspace || key.delete) {
|
|
228
|
+
if (state.alternativeCursorPosition <= 0) return state;
|
|
229
|
+
const nextText =
|
|
230
|
+
state.alternativeText.slice(0, state.alternativeCursorPosition - 1) +
|
|
231
|
+
state.alternativeText.slice(state.alternativeCursorPosition);
|
|
232
|
+
return {
|
|
233
|
+
...state,
|
|
234
|
+
selectedOption: "alternative",
|
|
235
|
+
alternativeText: nextText,
|
|
236
|
+
alternativeCursorPosition: state.alternativeCursorPosition - 1,
|
|
237
|
+
hasUserInput: nextText.length > 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return state;
|
|
242
|
+
}
|
|
71
243
|
default:
|
|
72
244
|
return state;
|
|
73
245
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
|
|
3
|
+
export interface SelectorState {
|
|
4
|
+
selectedIndex: number;
|
|
5
|
+
activeTab: "general" | "commands" | "custom-commands";
|
|
6
|
+
pendingDecision: "select" | "insert" | "cancel" | "tab-switch" | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type SelectorAction =
|
|
10
|
+
| { type: "MOVE_UP" }
|
|
11
|
+
| { type: "MOVE_DOWN"; maxIndex: number }
|
|
12
|
+
| { type: "RESET_INDEX" }
|
|
13
|
+
| { type: "SWITCH_TAB"; tabs: string[] }
|
|
14
|
+
| { type: "HANDLE_KEY"; key: Key; maxIndex: number; tabs: string[] }
|
|
15
|
+
| { type: "CLEAR_DECISION" };
|
|
16
|
+
|
|
17
|
+
export function helpSelectorReducer(
|
|
18
|
+
state: SelectorState,
|
|
19
|
+
action: SelectorAction,
|
|
20
|
+
): SelectorState {
|
|
21
|
+
switch (action.type) {
|
|
22
|
+
case "MOVE_UP":
|
|
23
|
+
return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
|
|
24
|
+
case "MOVE_DOWN":
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
|
|
28
|
+
};
|
|
29
|
+
case "RESET_INDEX":
|
|
30
|
+
return { ...state, selectedIndex: 0 };
|
|
31
|
+
case "SWITCH_TAB": {
|
|
32
|
+
const currentIndex = action.tabs.indexOf(state.activeTab);
|
|
33
|
+
const nextIndex = (currentIndex + 1) % action.tabs.length;
|
|
34
|
+
return {
|
|
35
|
+
...state,
|
|
36
|
+
activeTab: action.tabs[nextIndex] as
|
|
37
|
+
| "general"
|
|
38
|
+
| "commands"
|
|
39
|
+
| "custom-commands",
|
|
40
|
+
selectedIndex: 0,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
case "HANDLE_KEY":
|
|
44
|
+
if (action.key.upArrow) {
|
|
45
|
+
return {
|
|
46
|
+
...state,
|
|
47
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (action.key.downArrow) {
|
|
51
|
+
return {
|
|
52
|
+
...state,
|
|
53
|
+
selectedIndex: Math.min(action.maxIndex, state.selectedIndex + 1),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (action.key.tab) {
|
|
57
|
+
const currentIndex = action.tabs.indexOf(state.activeTab);
|
|
58
|
+
const nextIndex = (currentIndex + 1) % action.tabs.length;
|
|
59
|
+
return {
|
|
60
|
+
...state,
|
|
61
|
+
activeTab: action.tabs[nextIndex] as
|
|
62
|
+
| "general"
|
|
63
|
+
| "commands"
|
|
64
|
+
| "custom-commands",
|
|
65
|
+
selectedIndex: 0,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (action.key.escape) {
|
|
69
|
+
return { ...state, pendingDecision: "cancel" };
|
|
70
|
+
}
|
|
71
|
+
return state;
|
|
72
|
+
case "CLEAR_DECISION":
|
|
73
|
+
return { ...state, pendingDecision: null };
|
|
74
|
+
default:
|
|
75
|
+
return state;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,12 +1,28 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
|
|
3
|
+
export type PendingEffect =
|
|
4
|
+
| { type: "CANCEL" }
|
|
5
|
+
| { type: "CONNECT_SERVER"; serverName: string }
|
|
6
|
+
| { type: "DISCONNECT_SERVER"; serverName: string };
|
|
7
|
+
|
|
1
8
|
export interface McpManagerState {
|
|
2
9
|
selectedIndex: number;
|
|
3
10
|
viewMode: "list" | "detail";
|
|
11
|
+
pendingEffect: PendingEffect | null;
|
|
4
12
|
}
|
|
5
13
|
|
|
6
14
|
export type McpManagerAction =
|
|
7
15
|
| { type: "MOVE_UP"; serverCount: number }
|
|
8
16
|
| { type: "MOVE_DOWN"; serverCount: number }
|
|
9
|
-
| { type: "SET_VIEW_MODE"; viewMode: "list" | "detail" }
|
|
17
|
+
| { type: "SET_VIEW_MODE"; viewMode: "list" | "detail" }
|
|
18
|
+
| {
|
|
19
|
+
type: "HANDLE_KEY";
|
|
20
|
+
input: string;
|
|
21
|
+
key: Key;
|
|
22
|
+
serverCount: number;
|
|
23
|
+
servers: Array<{ name: string; status: string }>;
|
|
24
|
+
}
|
|
25
|
+
| { type: "CLEAR_PENDING_EFFECT" };
|
|
10
26
|
|
|
11
27
|
export function mcpManagerReducer(
|
|
12
28
|
state: McpManagerState,
|
|
@@ -25,6 +41,71 @@ export function mcpManagerReducer(
|
|
|
25
41
|
};
|
|
26
42
|
case "SET_VIEW_MODE":
|
|
27
43
|
return { ...state, viewMode: action.viewMode };
|
|
44
|
+
case "HANDLE_KEY": {
|
|
45
|
+
const { input, key, serverCount, servers } = action;
|
|
46
|
+
|
|
47
|
+
if (key.return) {
|
|
48
|
+
if (state.viewMode === "list") {
|
|
49
|
+
return { ...state, viewMode: "detail" };
|
|
50
|
+
}
|
|
51
|
+
return state;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (key.escape) {
|
|
55
|
+
if (state.viewMode === "detail") {
|
|
56
|
+
return { ...state, viewMode: "list" };
|
|
57
|
+
} else {
|
|
58
|
+
return { ...state, pendingEffect: { type: "CANCEL" } };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (key.upArrow) {
|
|
63
|
+
return {
|
|
64
|
+
...state,
|
|
65
|
+
selectedIndex: Math.max(0, state.selectedIndex - 1),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (key.downArrow) {
|
|
70
|
+
return {
|
|
71
|
+
...state,
|
|
72
|
+
selectedIndex: Math.min(serverCount - 1, state.selectedIndex + 1),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Hotkeys for server actions
|
|
77
|
+
if (input === "c") {
|
|
78
|
+
const server = servers[state.selectedIndex];
|
|
79
|
+
if (
|
|
80
|
+
server &&
|
|
81
|
+
(server.status === "disconnected" || server.status === "error")
|
|
82
|
+
) {
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
pendingEffect: { type: "CONNECT_SERVER", serverName: server.name },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return state;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (input === "d") {
|
|
92
|
+
const server = servers[state.selectedIndex];
|
|
93
|
+
if (server && server.status === "connected") {
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
pendingEffect: {
|
|
97
|
+
type: "DISCONNECT_SERVER",
|
|
98
|
+
serverName: server.name,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return state;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
case "CLEAR_PENDING_EFFECT":
|
|
108
|
+
return { ...state, pendingEffect: null };
|
|
28
109
|
default:
|
|
29
110
|
return state;
|
|
30
111
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { Key } from "ink";
|
|
2
|
+
|
|
1
3
|
export interface PluginDetailState {
|
|
2
4
|
selectedScopeIndex: number;
|
|
3
5
|
selectedActionIndex: number;
|
|
6
|
+
pendingDecision: "select" | "cancel" | null;
|
|
4
7
|
}
|
|
5
8
|
|
|
6
9
|
export type PluginDetailAction =
|
|
@@ -9,7 +12,9 @@ export type PluginDetailAction =
|
|
|
9
12
|
| { type: "MOVE_SCOPE_UP"; maxIndex: number }
|
|
10
13
|
| { type: "MOVE_SCOPE_DOWN"; maxIndex: number }
|
|
11
14
|
| { type: "MOVE_ACTION_UP"; maxIndex: number }
|
|
12
|
-
| { type: "MOVE_ACTION_DOWN"; maxIndex: number }
|
|
15
|
+
| { type: "MOVE_ACTION_DOWN"; maxIndex: number }
|
|
16
|
+
| { type: "HANDLE_KEY"; key: Key; maxIndex: number }
|
|
17
|
+
| { type: "CLEAR_DECISION" };
|
|
13
18
|
|
|
14
19
|
export function pluginDetailReducer(
|
|
15
20
|
state: PluginDetailState,
|
|
@@ -52,6 +57,42 @@ export function pluginDetailReducer(
|
|
|
52
57
|
? state.selectedActionIndex + 1
|
|
53
58
|
: 0,
|
|
54
59
|
};
|
|
60
|
+
case "HANDLE_KEY":
|
|
61
|
+
if (action.key.upArrow) {
|
|
62
|
+
return {
|
|
63
|
+
...state,
|
|
64
|
+
selectedScopeIndex:
|
|
65
|
+
state.selectedScopeIndex > 0
|
|
66
|
+
? state.selectedScopeIndex - 1
|
|
67
|
+
: action.maxIndex,
|
|
68
|
+
selectedActionIndex:
|
|
69
|
+
state.selectedActionIndex > 0
|
|
70
|
+
? state.selectedActionIndex - 1
|
|
71
|
+
: action.maxIndex,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (action.key.downArrow) {
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
selectedScopeIndex:
|
|
78
|
+
state.selectedScopeIndex < action.maxIndex
|
|
79
|
+
? state.selectedScopeIndex + 1
|
|
80
|
+
: 0,
|
|
81
|
+
selectedActionIndex:
|
|
82
|
+
state.selectedActionIndex < action.maxIndex
|
|
83
|
+
? state.selectedActionIndex + 1
|
|
84
|
+
: 0,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (action.key.return) {
|
|
88
|
+
return { ...state, pendingDecision: "select" };
|
|
89
|
+
}
|
|
90
|
+
if (action.key.escape) {
|
|
91
|
+
return { ...state, pendingDecision: "cancel" };
|
|
92
|
+
}
|
|
93
|
+
return state;
|
|
94
|
+
case "CLEAR_DECISION":
|
|
95
|
+
return { ...state, pendingDecision: null };
|
|
55
96
|
default:
|
|
56
97
|
return state;
|
|
57
98
|
}
|