wave-code 0.15.0 → 0.15.2
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 +73 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +38 -24
- package/dist/contexts/useChat.d.ts +1 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +15 -8
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -1
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +6 -1
- package/dist/managers/inputReducer.d.ts +0 -4
- package/dist/managers/inputReducer.d.ts.map +1 -1
- package/dist/managers/inputReducer.js +1 -1
- package/dist/print-cli.d.ts +2 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +6 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/acp/agent.ts +83 -4
- package/src/cli.tsx +2 -0
- package/src/components/App.tsx +73 -32
- package/src/contexts/useChat.tsx +23 -7
- package/src/hooks/useInputManager.ts +8 -10
- package/src/index.ts +39 -1
- package/src/managers/inputHandlers.ts +7 -6
- package/src/managers/inputReducer.ts +1 -6
- package/src/print-cli.ts +9 -0
- package/src/types.ts +3 -1
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
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.15.
|
|
45
|
+
"wave-agent-sdk": "0.15.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/react": "^19.1.8",
|
package/src/acp/agent.ts
CHANGED
|
@@ -49,8 +49,10 @@ import {
|
|
|
49
49
|
type ResourceLink,
|
|
50
50
|
type EmbeddedResource,
|
|
51
51
|
type ImageContent,
|
|
52
|
+
type McpServer,
|
|
52
53
|
AGENT_METHODS,
|
|
53
54
|
} from "@agentclientprotocol/sdk";
|
|
55
|
+
import type { McpServerConfig, McpServerStatus } from "wave-agent-sdk";
|
|
54
56
|
|
|
55
57
|
export class WaveAcpAgent implements AcpAgent {
|
|
56
58
|
private agents: Map<string, WaveAgent> = new Map();
|
|
@@ -150,6 +152,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
150
152
|
},
|
|
151
153
|
agentCapabilities: {
|
|
152
154
|
loadSession: true,
|
|
155
|
+
mcpCapabilities: { http: true, sse: true },
|
|
153
156
|
sessionCapabilities: {
|
|
154
157
|
list: {},
|
|
155
158
|
close: {},
|
|
@@ -169,14 +172,20 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
169
172
|
private async createAgent(
|
|
170
173
|
sessionId: string | undefined,
|
|
171
174
|
cwd: string,
|
|
175
|
+
mcpServers?: McpServer[],
|
|
172
176
|
): Promise<WaveAgent> {
|
|
173
177
|
const callbacks: AgentOptions["callbacks"] = {};
|
|
174
178
|
const agentRef: { instance?: WaveAgent } = {};
|
|
175
179
|
|
|
180
|
+
const sdkMcpServers = mcpServers
|
|
181
|
+
? convertAcpMcpServers(mcpServers)
|
|
182
|
+
: undefined;
|
|
183
|
+
|
|
176
184
|
const agent = await WaveAgent.create({
|
|
177
185
|
workdir: cwd,
|
|
178
186
|
restoreSessionId: sessionId,
|
|
179
187
|
stream: true,
|
|
188
|
+
mcpServers: sdkMcpServers,
|
|
180
189
|
canUseTool: (context) => {
|
|
181
190
|
if (!agentRef.instance) {
|
|
182
191
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -202,6 +211,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
202
211
|
callbacks.onPermissionModeChange?.(mode),
|
|
203
212
|
onModelChange: (model) => callbacks.onModelChange?.(model),
|
|
204
213
|
onUserMessageAdded: (params) => callbacks.onUserMessageAdded?.(params),
|
|
214
|
+
onMcpServersChange: (servers) => callbacks.onMcpServersChange?.(servers),
|
|
205
215
|
},
|
|
206
216
|
});
|
|
207
217
|
|
|
@@ -234,9 +244,9 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
234
244
|
}
|
|
235
245
|
|
|
236
246
|
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
237
|
-
const { cwd } = params;
|
|
247
|
+
const { cwd, mcpServers } = params;
|
|
238
248
|
logger.info(`Creating new session in ${cwd}`);
|
|
239
|
-
const agent = await this.createAgent(undefined, cwd);
|
|
249
|
+
const agent = await this.createAgent(undefined, cwd, mcpServers);
|
|
240
250
|
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
241
251
|
|
|
242
252
|
return {
|
|
@@ -247,9 +257,9 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
247
257
|
}
|
|
248
258
|
|
|
249
259
|
async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
|
250
|
-
const { sessionId, cwd } = params;
|
|
260
|
+
const { sessionId, cwd, mcpServers } = params;
|
|
251
261
|
logger.info(`Loading session: ${sessionId} in ${cwd}`);
|
|
252
|
-
const agent = await this.createAgent(sessionId, cwd);
|
|
262
|
+
const agent = await this.createAgent(sessionId, cwd, mcpServers);
|
|
253
263
|
|
|
254
264
|
return {
|
|
255
265
|
modes: this.getSessionModeState(agent),
|
|
@@ -1002,6 +1012,75 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
1002
1012
|
},
|
|
1003
1013
|
});
|
|
1004
1014
|
},
|
|
1015
|
+
onMcpServersChange: (servers: McpServerStatus[]) => {
|
|
1016
|
+
for (const server of servers) {
|
|
1017
|
+
this.connection.sessionUpdate({
|
|
1018
|
+
sessionId: sessionId as AcpSessionId,
|
|
1019
|
+
update: {
|
|
1020
|
+
sessionUpdate: "ext_notification" as const,
|
|
1021
|
+
method: "mcp_server_status",
|
|
1022
|
+
params: {
|
|
1023
|
+
name: server.name,
|
|
1024
|
+
status: server.status,
|
|
1025
|
+
toolCount: server.toolCount,
|
|
1026
|
+
error: server.error,
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1005
1032
|
};
|
|
1006
1033
|
}
|
|
1007
1034
|
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Convert ACP McpServer[] to SDK Record<string, McpServerConfig>.
|
|
1038
|
+
*/
|
|
1039
|
+
function convertAcpMcpServers(
|
|
1040
|
+
servers: McpServer[],
|
|
1041
|
+
): Record<string, McpServerConfig> {
|
|
1042
|
+
const result: Record<string, McpServerConfig> = {};
|
|
1043
|
+
for (const server of servers) {
|
|
1044
|
+
const config: McpServerConfig = {};
|
|
1045
|
+
if ("type" in server && server.type === "http") {
|
|
1046
|
+
config.url = server.url;
|
|
1047
|
+
config.headers = convertHttpHeaders(server.headers);
|
|
1048
|
+
} else if ("type" in server && server.type === "sse") {
|
|
1049
|
+
config.url = server.url;
|
|
1050
|
+
config.headers = convertHttpHeaders(server.headers);
|
|
1051
|
+
} else {
|
|
1052
|
+
// stdio (no type discriminator)
|
|
1053
|
+
config.command = server.command;
|
|
1054
|
+
config.args = server.args;
|
|
1055
|
+
config.env = convertEnvVariables(server.env);
|
|
1056
|
+
}
|
|
1057
|
+
result[server.name] = config;
|
|
1058
|
+
}
|
|
1059
|
+
return result;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Convert ACP EnvVariable[] to SDK Record<string, string>.
|
|
1064
|
+
*/
|
|
1065
|
+
function convertEnvVariables(
|
|
1066
|
+
env: Array<{ name: string; value: string }>,
|
|
1067
|
+
): Record<string, string> {
|
|
1068
|
+
const result: Record<string, string> = {};
|
|
1069
|
+
for (const entry of env) {
|
|
1070
|
+
result[entry.name] = entry.value;
|
|
1071
|
+
}
|
|
1072
|
+
return result;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Convert ACP HttpHeader[] to SDK Record<string, string>.
|
|
1077
|
+
*/
|
|
1078
|
+
function convertHttpHeaders(
|
|
1079
|
+
headers: Array<{ name: string; value: string }>,
|
|
1080
|
+
): Record<string, string> {
|
|
1081
|
+
const result: Record<string, string> = {};
|
|
1082
|
+
for (const entry of headers) {
|
|
1083
|
+
result[entry.name] = entry.value;
|
|
1084
|
+
}
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
package/src/cli.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
24
24
|
workdir,
|
|
25
25
|
version,
|
|
26
26
|
model,
|
|
27
|
+
mcpServers,
|
|
27
28
|
} = options;
|
|
28
29
|
|
|
29
30
|
// Continue with ink-based UI for normal mode
|
|
@@ -49,6 +50,7 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
49
50
|
workdir={workdir}
|
|
50
51
|
version={version}
|
|
51
52
|
model={model}
|
|
53
|
+
mcpServers={mcpServers}
|
|
52
54
|
onExit={handleExit}
|
|
53
55
|
/>,
|
|
54
56
|
{ exitOnCtrlC: false },
|
package/src/components/App.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from "react";
|
|
2
2
|
import { useInput } from "ink";
|
|
3
3
|
import { ChatInterface } from "./ChatInterface.js";
|
|
4
|
-
import { ChatProvider } from "../contexts/useChat.js";
|
|
4
|
+
import { ChatProvider, useChat } from "../contexts/useChat.js";
|
|
5
5
|
import { AppProvider } from "../contexts/useAppConfig.js";
|
|
6
6
|
import { WorktreeExitPrompt } from "./WorktreeExitPrompt.js";
|
|
7
7
|
import {
|
|
@@ -21,19 +21,12 @@ interface AppWithProvidersProps extends BaseAppProps {
|
|
|
21
21
|
onExit: (shouldRemove: boolean) => void;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
disallowedTools,
|
|
31
|
-
worktreeSession,
|
|
32
|
-
workdir,
|
|
33
|
-
version,
|
|
34
|
-
model,
|
|
35
|
-
onExit,
|
|
36
|
-
}) => {
|
|
24
|
+
/** Wraps ChatInterface with worktree exit handling, using useChat() for hook access. */
|
|
25
|
+
const ChatWithExitPrompt: React.FC<{
|
|
26
|
+
worktreeSession: NonNullable<BaseAppProps["worktreeSession"]>;
|
|
27
|
+
onExit: (shouldRemove: boolean) => void;
|
|
28
|
+
}> = ({ worktreeSession, onExit }) => {
|
|
29
|
+
const { triggerWorktreeRemoveHook } = useChat();
|
|
37
30
|
const [isExiting, setIsExiting] = useState(false);
|
|
38
31
|
const [worktreeStatus, setWorktreeStatus] = useState<{
|
|
39
32
|
hasUncommittedChanges: boolean;
|
|
@@ -41,25 +34,21 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
41
34
|
} | null>(null);
|
|
42
35
|
|
|
43
36
|
const handleSignal = useCallback(async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
37
|
+
const cwd = worktreeSession.path;
|
|
38
|
+
const baseBranch = getDefaultRemoteBranch(cwd);
|
|
39
|
+
const hasChanges = hasUncommittedChanges(cwd);
|
|
40
|
+
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else {
|
|
57
|
-
onExit(true);
|
|
58
|
-
}
|
|
42
|
+
if (hasChanges || hasCommits) {
|
|
43
|
+
setWorktreeStatus({
|
|
44
|
+
hasUncommittedChanges: hasChanges,
|
|
45
|
+
hasNewCommits: hasCommits,
|
|
46
|
+
});
|
|
47
|
+
setIsExiting(true);
|
|
59
48
|
} else {
|
|
60
|
-
onExit(
|
|
49
|
+
onExit(true);
|
|
61
50
|
}
|
|
62
|
-
}, [worktreeSession,
|
|
51
|
+
}, [worktreeSession, onExit]);
|
|
63
52
|
|
|
64
53
|
useInput((input, key) => {
|
|
65
54
|
if (input === "c" && key.ctrl) {
|
|
@@ -80,7 +69,7 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
80
69
|
};
|
|
81
70
|
}, [handleSignal]);
|
|
82
71
|
|
|
83
|
-
if (isExiting &&
|
|
72
|
+
if (isExiting && worktreeStatus) {
|
|
84
73
|
return (
|
|
85
74
|
<WorktreeExitPrompt
|
|
86
75
|
name={worktreeSession.name}
|
|
@@ -88,12 +77,61 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
88
77
|
hasUncommittedChanges={worktreeStatus.hasUncommittedChanges}
|
|
89
78
|
hasNewCommits={worktreeStatus.hasNewCommits}
|
|
90
79
|
onKeep={() => onExit(false)}
|
|
91
|
-
onRemove={() =>
|
|
80
|
+
onRemove={async () => {
|
|
81
|
+
await triggerWorktreeRemoveHook(worktreeSession.path);
|
|
82
|
+
onExit(true);
|
|
83
|
+
}}
|
|
92
84
|
onCancel={() => setIsExiting(false)}
|
|
93
85
|
/>
|
|
94
86
|
);
|
|
95
87
|
}
|
|
96
88
|
|
|
89
|
+
return <ChatInterface />;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
93
|
+
bypassPermissions,
|
|
94
|
+
permissionMode,
|
|
95
|
+
pluginDirs,
|
|
96
|
+
tools,
|
|
97
|
+
allowedTools,
|
|
98
|
+
disallowedTools,
|
|
99
|
+
worktreeSession,
|
|
100
|
+
workdir,
|
|
101
|
+
version,
|
|
102
|
+
model,
|
|
103
|
+
mcpServers,
|
|
104
|
+
onExit,
|
|
105
|
+
}) => {
|
|
106
|
+
// Handle Ctrl-C for non-worktree sessions (immediate exit)
|
|
107
|
+
// Ink runs terminal in raw mode, so Ctrl+C arrives as useInput event, not SIGINT
|
|
108
|
+
useInput((input, key) => {
|
|
109
|
+
if (!worktreeSession && input === "c" && key.ctrl) {
|
|
110
|
+
onExit(false);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (worktreeSession) {
|
|
116
|
+
return (
|
|
117
|
+
<ChatProvider
|
|
118
|
+
bypassPermissions={bypassPermissions}
|
|
119
|
+
permissionMode={permissionMode}
|
|
120
|
+
pluginDirs={pluginDirs}
|
|
121
|
+
tools={tools}
|
|
122
|
+
allowedTools={allowedTools}
|
|
123
|
+
disallowedTools={disallowedTools}
|
|
124
|
+
workdir={workdir}
|
|
125
|
+
worktreeSession={worktreeSession}
|
|
126
|
+
version={version}
|
|
127
|
+
model={model}
|
|
128
|
+
mcpServers={mcpServers}
|
|
129
|
+
>
|
|
130
|
+
<ChatWithExitPrompt worktreeSession={worktreeSession} onExit={onExit} />
|
|
131
|
+
</ChatProvider>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
97
135
|
return (
|
|
98
136
|
<ChatProvider
|
|
99
137
|
bypassPermissions={bypassPermissions}
|
|
@@ -106,6 +144,7 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
106
144
|
worktreeSession={worktreeSession}
|
|
107
145
|
version={version}
|
|
108
146
|
model={model}
|
|
147
|
+
mcpServers={mcpServers}
|
|
109
148
|
>
|
|
110
149
|
<ChatInterface />
|
|
111
150
|
</ChatProvider>
|
|
@@ -125,6 +164,7 @@ export const App: React.FC<AppProps> = ({
|
|
|
125
164
|
workdir,
|
|
126
165
|
version,
|
|
127
166
|
model,
|
|
167
|
+
mcpServers,
|
|
128
168
|
onExit,
|
|
129
169
|
}) => {
|
|
130
170
|
return (
|
|
@@ -143,6 +183,7 @@ export const App: React.FC<AppProps> = ({
|
|
|
143
183
|
workdir={workdir}
|
|
144
184
|
version={version}
|
|
145
185
|
model={model}
|
|
186
|
+
mcpServers={mcpServers}
|
|
146
187
|
onExit={onExit}
|
|
147
188
|
/>
|
|
148
189
|
</AppProvider>
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -119,6 +119,8 @@ export interface ChatContextType {
|
|
|
119
119
|
workdir?: string;
|
|
120
120
|
// Agent recreation (e.g. after plugin install)
|
|
121
121
|
recreateAgent: () => void;
|
|
122
|
+
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
123
|
+
triggerWorktreeRemoveHook: (worktreePath: string) => Promise<void>;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -147,6 +149,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
147
149
|
worktreeSession,
|
|
148
150
|
version,
|
|
149
151
|
model,
|
|
152
|
+
mcpServers,
|
|
150
153
|
}) => {
|
|
151
154
|
const { restoreSessionId, continueLastSession } = useAppConfig();
|
|
152
155
|
const { stdout } = useStdout();
|
|
@@ -198,7 +201,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
198
201
|
const [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);
|
|
199
202
|
|
|
200
203
|
// MCP State
|
|
201
|
-
const [
|
|
204
|
+
const [mcpServerStatuses, setMcpServerStatuses] = useState<McpServerStatus[]>(
|
|
205
|
+
[],
|
|
206
|
+
);
|
|
202
207
|
|
|
203
208
|
// Background tasks state
|
|
204
209
|
const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
|
|
@@ -328,8 +333,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
328
333
|
onMessagesChange: () => {
|
|
329
334
|
throttledSetMessages();
|
|
330
335
|
},
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
onMcpServersChange: (servers) => {
|
|
337
|
+
setMcpServerStatuses([...servers]);
|
|
333
338
|
},
|
|
334
339
|
onSessionIdChange: (sessionId) => {
|
|
335
340
|
setSessionId(sessionId);
|
|
@@ -403,6 +408,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
403
408
|
worktreeName: worktreeSession?.name,
|
|
404
409
|
isNewWorktree: worktreeSession?.isNew,
|
|
405
410
|
model,
|
|
411
|
+
mcpServers,
|
|
406
412
|
});
|
|
407
413
|
|
|
408
414
|
agentRef.current = agent;
|
|
@@ -420,8 +426,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
420
426
|
setConfiguredModels(agent.getConfiguredModels());
|
|
421
427
|
|
|
422
428
|
// Get initial MCP servers state
|
|
423
|
-
const
|
|
424
|
-
|
|
429
|
+
const initialMcpServers = agent.getMcpServers?.() || [];
|
|
430
|
+
setMcpServerStatuses(initialMcpServers);
|
|
425
431
|
|
|
426
432
|
// Get initial commands
|
|
427
433
|
const agentSlashCommands = agent.getSlashCommands?.() || [];
|
|
@@ -444,6 +450,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
444
450
|
model,
|
|
445
451
|
initialPermissionMode,
|
|
446
452
|
throttledSetMessages,
|
|
453
|
+
mcpServers,
|
|
447
454
|
],
|
|
448
455
|
);
|
|
449
456
|
|
|
@@ -459,7 +466,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
459
466
|
}
|
|
460
467
|
agentRef.current = null;
|
|
461
468
|
setMessages([]);
|
|
462
|
-
|
|
469
|
+
setMcpServerStatuses([]);
|
|
463
470
|
setSlashCommands([]);
|
|
464
471
|
setSessionId("");
|
|
465
472
|
setIsLoading(false);
|
|
@@ -494,6 +501,14 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
494
501
|
};
|
|
495
502
|
}, []);
|
|
496
503
|
|
|
504
|
+
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
505
|
+
const triggerWorktreeRemoveHook = useCallback(
|
|
506
|
+
async (worktreePath: string) => {
|
|
507
|
+
await agentRef.current?.triggerWorktreeRemoveHook(worktreePath);
|
|
508
|
+
},
|
|
509
|
+
[],
|
|
510
|
+
);
|
|
511
|
+
|
|
497
512
|
// Send message function (including judgment logic)
|
|
498
513
|
const sendMessage = useCallback(
|
|
499
514
|
async (
|
|
@@ -737,7 +752,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
737
752
|
getConfiguredModels,
|
|
738
753
|
setModel,
|
|
739
754
|
isCompacting,
|
|
740
|
-
mcpServers,
|
|
755
|
+
mcpServers: mcpServerStatuses,
|
|
741
756
|
connectMcpServer,
|
|
742
757
|
disconnectMcpServer,
|
|
743
758
|
backgroundTasks,
|
|
@@ -767,6 +782,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
767
782
|
version,
|
|
768
783
|
workdir,
|
|
769
784
|
recreateAgent,
|
|
785
|
+
triggerWorktreeRemoveHook,
|
|
770
786
|
};
|
|
771
787
|
|
|
772
788
|
return (
|
|
@@ -119,6 +119,14 @@ export const useInputManager = (
|
|
|
119
119
|
effect.images,
|
|
120
120
|
effect.longTextMap,
|
|
121
121
|
);
|
|
122
|
+
PromptHistoryManager.addEntry(
|
|
123
|
+
effect.content,
|
|
124
|
+
sessionId,
|
|
125
|
+
effect.longTextMap,
|
|
126
|
+
workdir,
|
|
127
|
+
).catch((err: unknown) => {
|
|
128
|
+
logger?.error("Failed to save prompt history", err);
|
|
129
|
+
});
|
|
122
130
|
break;
|
|
123
131
|
case "ABORT_MESSAGE":
|
|
124
132
|
onAbortMessage?.();
|
|
@@ -148,16 +156,6 @@ export const useInputManager = (
|
|
|
148
156
|
case "PERMISSION_MODE_CHANGE":
|
|
149
157
|
onPermissionModeChange?.(effect.mode);
|
|
150
158
|
break;
|
|
151
|
-
case "SAVE_HISTORY":
|
|
152
|
-
PromptHistoryManager.addEntry(
|
|
153
|
-
effect.content,
|
|
154
|
-
sessionId,
|
|
155
|
-
effect.longTextMap,
|
|
156
|
-
workdir,
|
|
157
|
-
).catch((err: unknown) => {
|
|
158
|
-
logger?.error("Failed to save prompt history", err);
|
|
159
|
-
});
|
|
160
|
-
break;
|
|
161
159
|
case "FETCH_HISTORY": {
|
|
162
160
|
let sessionIds: string[] | undefined = sessionId
|
|
163
161
|
? [sessionId]
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import yargs from "yargs";
|
|
2
2
|
import { hideBin } from "yargs/helpers";
|
|
3
3
|
import { startCli } from "./cli.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Scope,
|
|
6
|
+
generateRandomName,
|
|
7
|
+
type PermissionMode,
|
|
8
|
+
setCurrentWorktreeSession,
|
|
9
|
+
} from "wave-agent-sdk";
|
|
5
10
|
import { createWorktree, type WorktreeSession } from "./utils/worktree.js";
|
|
6
11
|
import path from "path";
|
|
7
12
|
import { readFileSync } from "fs";
|
|
@@ -94,6 +99,12 @@ export async function main() {
|
|
|
94
99
|
type: "string",
|
|
95
100
|
global: false,
|
|
96
101
|
})
|
|
102
|
+
.option("mcp-config", {
|
|
103
|
+
description:
|
|
104
|
+
"MCP server configuration as JSON string (same format as .mcp.json)",
|
|
105
|
+
type: "string",
|
|
106
|
+
global: false,
|
|
107
|
+
})
|
|
97
108
|
.option("acp", {
|
|
98
109
|
description: "Run as an ACP bridge",
|
|
99
110
|
type: "boolean",
|
|
@@ -284,6 +295,20 @@ export async function main() {
|
|
|
284
295
|
argv.disallowedTools as string | undefined,
|
|
285
296
|
);
|
|
286
297
|
|
|
298
|
+
// Parse MCP server configuration from --mcp-config JSON string
|
|
299
|
+
let mcpServers:
|
|
300
|
+
| Record<string, import("wave-agent-sdk").McpServerConfig>
|
|
301
|
+
| undefined;
|
|
302
|
+
if (argv.mcpConfig) {
|
|
303
|
+
try {
|
|
304
|
+
const parsed = JSON.parse(argv.mcpConfig as string);
|
|
305
|
+
mcpServers = parsed.mcpServers || parsed;
|
|
306
|
+
} catch {
|
|
307
|
+
console.error("Failed to parse --mcp-config as JSON");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
287
312
|
// Resolve plugin directories to absolute paths before any worktree logic
|
|
288
313
|
const pluginDirs = (argv.pluginDir as string[] | undefined)?.map((dir) =>
|
|
289
314
|
path.resolve(originalCwd, dir),
|
|
@@ -300,6 +325,16 @@ export async function main() {
|
|
|
300
325
|
name = generateRandomName();
|
|
301
326
|
}
|
|
302
327
|
worktreeSession = createWorktree(name, originalCwd);
|
|
328
|
+
|
|
329
|
+
// Register worktree session so system prompt includes worktree warnings
|
|
330
|
+
setCurrentWorktreeSession({
|
|
331
|
+
originalCwd,
|
|
332
|
+
worktreePath: worktreeSession.path,
|
|
333
|
+
worktreeBranch: worktreeSession.branch,
|
|
334
|
+
worktreeName: worktreeSession.name,
|
|
335
|
+
isNew: worktreeSession.isNew,
|
|
336
|
+
repoRoot: worktreeSession.repoRoot,
|
|
337
|
+
});
|
|
303
338
|
}
|
|
304
339
|
|
|
305
340
|
const workdir = worktreeSession?.path || originalCwd;
|
|
@@ -341,6 +376,7 @@ export async function main() {
|
|
|
341
376
|
workdir,
|
|
342
377
|
version,
|
|
343
378
|
model: argv.model as string | undefined,
|
|
379
|
+
mcpServers,
|
|
344
380
|
});
|
|
345
381
|
}
|
|
346
382
|
|
|
@@ -364,6 +400,7 @@ export async function main() {
|
|
|
364
400
|
workdir,
|
|
365
401
|
version,
|
|
366
402
|
model: argv.model as string | undefined,
|
|
403
|
+
mcpServers,
|
|
367
404
|
});
|
|
368
405
|
}
|
|
369
406
|
|
|
@@ -380,6 +417,7 @@ export async function main() {
|
|
|
380
417
|
workdir,
|
|
381
418
|
version,
|
|
382
419
|
model: argv.model as string | undefined,
|
|
420
|
+
mcpServers,
|
|
383
421
|
});
|
|
384
422
|
} catch (error) {
|
|
385
423
|
console.error("Failed to start WAVE Code:", error);
|
|
@@ -125,7 +125,12 @@ export const cyclePermissionMode = (
|
|
|
125
125
|
dispatch: React.Dispatch<InputAction>,
|
|
126
126
|
callbacks: Partial<InputManagerCallbacks>,
|
|
127
127
|
) => {
|
|
128
|
-
const modes: PermissionMode[] = [
|
|
128
|
+
const modes: PermissionMode[] = [
|
|
129
|
+
"default",
|
|
130
|
+
"acceptEdits",
|
|
131
|
+
"bypassPermissions",
|
|
132
|
+
"plan",
|
|
133
|
+
];
|
|
129
134
|
const currentIndex = modes.indexOf(currentMode);
|
|
130
135
|
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
|
|
131
136
|
const nextMode = modes[nextIndex];
|
|
@@ -760,11 +765,7 @@ export const handleInput = async (
|
|
|
760
765
|
}
|
|
761
766
|
|
|
762
767
|
if (key.tab && key.shift) {
|
|
763
|
-
cyclePermissionMode(
|
|
764
|
-
state.permissionMode,
|
|
765
|
-
dispatch,
|
|
766
|
-
callbacks,
|
|
767
|
-
);
|
|
768
|
+
cyclePermissionMode(state.permissionMode, dispatch, callbacks);
|
|
768
769
|
return true;
|
|
769
770
|
}
|
|
770
771
|
|
|
@@ -38,11 +38,6 @@ export type PendingEffect =
|
|
|
38
38
|
| { type: "BACKGROUND_CURRENT_TASK" }
|
|
39
39
|
| { type: "ASK_BTW"; question: string }
|
|
40
40
|
| { type: "PERMISSION_MODE_CHANGE"; mode: PermissionMode }
|
|
41
|
-
| {
|
|
42
|
-
type: "SAVE_HISTORY";
|
|
43
|
-
content: string;
|
|
44
|
-
longTextMap: Record<string, string>;
|
|
45
|
-
}
|
|
46
41
|
| { type: "FETCH_HISTORY" }
|
|
47
42
|
| { type: "PASTE_IMAGE" }
|
|
48
43
|
| { type: "EXECUTE_COMMAND"; command: string };
|
|
@@ -768,8 +763,8 @@ export function inputReducer(
|
|
|
768
763
|
const modes: PermissionMode[] = [
|
|
769
764
|
"default",
|
|
770
765
|
"acceptEdits",
|
|
771
|
-
"plan",
|
|
772
766
|
"bypassPermissions",
|
|
767
|
+
"plan",
|
|
773
768
|
];
|
|
774
769
|
const currentIndex = modes.indexOf(state.permissionMode);
|
|
775
770
|
const nextIndex =
|
package/src/print-cli.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface PrintCliOptions extends BaseAppProps {
|
|
|
14
14
|
continueLastSession?: boolean;
|
|
15
15
|
message?: string;
|
|
16
16
|
showStats?: boolean;
|
|
17
|
+
/** MCP server config parsed from --mcp-config CLI argument */
|
|
18
|
+
mcpServers?: Record<string, import("wave-agent-sdk").McpServerConfig>;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
function displayTimingInfo(startTime: Date, showStats: boolean): void {
|
|
@@ -46,6 +48,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
46
48
|
worktreeSession,
|
|
47
49
|
workdir,
|
|
48
50
|
model,
|
|
51
|
+
mcpServers,
|
|
49
52
|
} = options;
|
|
50
53
|
|
|
51
54
|
if (
|
|
@@ -158,6 +161,7 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
158
161
|
disallowedTools,
|
|
159
162
|
workdir,
|
|
160
163
|
model,
|
|
164
|
+
mcpServers,
|
|
161
165
|
// 保持流式模式以获得更好的命令行用户体验
|
|
162
166
|
});
|
|
163
167
|
|
|
@@ -167,6 +171,11 @@ export async function startPrintCli(options: PrintCliOptions): Promise<void> {
|
|
|
167
171
|
process.stdout.write("\n");
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
// Wait for running background tasks and subagents to complete
|
|
175
|
+
while (agent.hasRunningBackgroundWork) {
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
177
|
+
}
|
|
178
|
+
|
|
170
179
|
// Display usage summary before exit
|
|
171
180
|
if (showStats) {
|
|
172
181
|
try {
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorktreeSession } from "./utils/worktree.js";
|
|
2
|
-
import { PermissionMode } from "wave-agent-sdk";
|
|
2
|
+
import { PermissionMode, McpServerConfig } from "wave-agent-sdk";
|
|
3
3
|
|
|
4
4
|
export interface BaseAppProps {
|
|
5
5
|
bypassPermissions?: boolean;
|
|
@@ -12,4 +12,6 @@ export interface BaseAppProps {
|
|
|
12
12
|
workdir?: string;
|
|
13
13
|
version?: string;
|
|
14
14
|
model?: string;
|
|
15
|
+
/** Optional MCP server configs to pass to Agent.create() */
|
|
16
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
15
17
|
}
|