wave-code 0.15.1 → 0.15.3
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/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.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 +2 -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/index.ts +39 -1
- package/src/managers/inputHandlers.ts +7 -6
- package/src/managers/inputReducer.ts +1 -1
- package/src/print-cli.ts +4 -0
- package/src/types.ts +3 -1
package/dist/acp/agent.d.ts.map
CHANGED
|
@@ -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,
|
|
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,EAOpC,MAAM,0BAA0B,CAAC;AAGlC,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;IAyBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IA0EnB,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;CAyOxB"}
|
package/dist/acp/agent.js
CHANGED
|
@@ -89,6 +89,7 @@ export class WaveAcpAgent {
|
|
|
89
89
|
},
|
|
90
90
|
agentCapabilities: {
|
|
91
91
|
loadSession: true,
|
|
92
|
+
mcpCapabilities: { http: true, sse: true },
|
|
92
93
|
sessionCapabilities: {
|
|
93
94
|
list: {},
|
|
94
95
|
close: {},
|
|
@@ -103,13 +104,17 @@ export class WaveAcpAgent {
|
|
|
103
104
|
async authenticate() {
|
|
104
105
|
// No authentication required for now
|
|
105
106
|
}
|
|
106
|
-
async createAgent(sessionId, cwd) {
|
|
107
|
+
async createAgent(sessionId, cwd, mcpServers) {
|
|
107
108
|
const callbacks = {};
|
|
108
109
|
const agentRef = {};
|
|
110
|
+
const sdkMcpServers = mcpServers
|
|
111
|
+
? convertAcpMcpServers(mcpServers)
|
|
112
|
+
: undefined;
|
|
109
113
|
const agent = await WaveAgent.create({
|
|
110
114
|
workdir: cwd,
|
|
111
115
|
restoreSessionId: sessionId,
|
|
112
116
|
stream: true,
|
|
117
|
+
mcpServers: sdkMcpServers,
|
|
113
118
|
canUseTool: (context) => {
|
|
114
119
|
if (!agentRef.instance) {
|
|
115
120
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -127,6 +132,7 @@ export class WaveAcpAgent {
|
|
|
127
132
|
onPermissionModeChange: (mode) => callbacks.onPermissionModeChange?.(mode),
|
|
128
133
|
onModelChange: (model) => callbacks.onModelChange?.(model),
|
|
129
134
|
onUserMessageAdded: (params) => callbacks.onUserMessageAdded?.(params),
|
|
135
|
+
onMcpServersChange: (servers) => callbacks.onMcpServersChange?.(servers),
|
|
130
136
|
},
|
|
131
137
|
});
|
|
132
138
|
agentRef.instance = agent;
|
|
@@ -154,9 +160,9 @@ export class WaveAcpAgent {
|
|
|
154
160
|
return agent;
|
|
155
161
|
}
|
|
156
162
|
async newSession(params) {
|
|
157
|
-
const { cwd } = params;
|
|
163
|
+
const { cwd, mcpServers } = params;
|
|
158
164
|
logger.info(`Creating new session in ${cwd}`);
|
|
159
|
-
const agent = await this.createAgent(undefined, cwd);
|
|
165
|
+
const agent = await this.createAgent(undefined, cwd, mcpServers);
|
|
160
166
|
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
161
167
|
return {
|
|
162
168
|
sessionId: agent.sessionId,
|
|
@@ -165,9 +171,9 @@ export class WaveAcpAgent {
|
|
|
165
171
|
};
|
|
166
172
|
}
|
|
167
173
|
async loadSession(params) {
|
|
168
|
-
const { sessionId, cwd } = params;
|
|
174
|
+
const { sessionId, cwd, mcpServers } = params;
|
|
169
175
|
logger.info(`Loading session: ${sessionId} in ${cwd}`);
|
|
170
|
-
const agent = await this.createAgent(sessionId, cwd);
|
|
176
|
+
const agent = await this.createAgent(sessionId, cwd, mcpServers);
|
|
171
177
|
return {
|
|
172
178
|
modes: this.getSessionModeState(agent),
|
|
173
179
|
configOptions: this.getSessionConfigOptions(agent),
|
|
@@ -787,6 +793,68 @@ export class WaveAcpAgent {
|
|
|
787
793
|
},
|
|
788
794
|
});
|
|
789
795
|
},
|
|
796
|
+
onMcpServersChange: (servers) => {
|
|
797
|
+
for (const server of servers) {
|
|
798
|
+
this.connection.sessionUpdate({
|
|
799
|
+
sessionId: sessionId,
|
|
800
|
+
update: {
|
|
801
|
+
sessionUpdate: "ext_notification",
|
|
802
|
+
method: "mcp_server_status",
|
|
803
|
+
params: {
|
|
804
|
+
name: server.name,
|
|
805
|
+
status: server.status,
|
|
806
|
+
toolCount: server.toolCount,
|
|
807
|
+
error: server.error,
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
},
|
|
790
813
|
};
|
|
791
814
|
}
|
|
792
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Convert ACP McpServer[] to SDK Record<string, McpServerConfig>.
|
|
818
|
+
*/
|
|
819
|
+
function convertAcpMcpServers(servers) {
|
|
820
|
+
const result = {};
|
|
821
|
+
for (const server of servers) {
|
|
822
|
+
const config = {};
|
|
823
|
+
if ("type" in server && server.type === "http") {
|
|
824
|
+
config.url = server.url;
|
|
825
|
+
config.headers = convertHttpHeaders(server.headers);
|
|
826
|
+
}
|
|
827
|
+
else if ("type" in server && server.type === "sse") {
|
|
828
|
+
config.url = server.url;
|
|
829
|
+
config.headers = convertHttpHeaders(server.headers);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
// stdio (no type discriminator)
|
|
833
|
+
config.command = server.command;
|
|
834
|
+
config.args = server.args;
|
|
835
|
+
config.env = convertEnvVariables(server.env);
|
|
836
|
+
}
|
|
837
|
+
result[server.name] = config;
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Convert ACP EnvVariable[] to SDK Record<string, string>.
|
|
843
|
+
*/
|
|
844
|
+
function convertEnvVariables(env) {
|
|
845
|
+
const result = {};
|
|
846
|
+
for (const entry of env) {
|
|
847
|
+
result[entry.name] = entry.value;
|
|
848
|
+
}
|
|
849
|
+
return result;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Convert ACP HttpHeader[] to SDK Record<string, string>.
|
|
853
|
+
*/
|
|
854
|
+
function convertHttpHeaders(headers) {
|
|
855
|
+
const result = {};
|
|
856
|
+
for (const entry of headers) {
|
|
857
|
+
result[entry.name] = entry.value;
|
|
858
|
+
}
|
|
859
|
+
return result;
|
|
860
|
+
}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkEjE"}
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { App } from "./components/App.js";
|
|
|
4
4
|
import { cleanupLogs } from "./utils/logger.js";
|
|
5
5
|
import { removeWorktree } from "./utils/worktree.js";
|
|
6
6
|
export async function startCli(options) {
|
|
7
|
-
const { restoreSessionId, continueLastSession, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, } = options;
|
|
7
|
+
const { restoreSessionId, continueLastSession, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, mcpServers, } = options;
|
|
8
8
|
// Continue with ink-based UI for normal mode
|
|
9
9
|
let shouldRemoveWorktree = false;
|
|
10
10
|
const handleExit = (shouldRemove) => {
|
|
@@ -12,7 +12,7 @@ export async function startCli(options) {
|
|
|
12
12
|
unmount();
|
|
13
13
|
};
|
|
14
14
|
// Render the application
|
|
15
|
-
const { unmount, waitUntilExit } = render(_jsx(App, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, onExit: handleExit }), { exitOnCtrlC: false });
|
|
15
|
+
const { unmount, waitUntilExit } = render(_jsx(App, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, mcpServers: mcpServers, onExit: handleExit }), { exitOnCtrlC: false });
|
|
16
16
|
// Wait for the app to finish unmounting
|
|
17
17
|
await waitUntilExit();
|
|
18
18
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAWhE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,QAAS,SAAQ,YAAY;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAWhE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,QAAS,SAAQ,YAAY;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAwID,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAqClC,CAAC"}
|
package/dist/components/App.js
CHANGED
|
@@ -2,34 +2,31 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState, useEffect, useCallback } from "react";
|
|
3
3
|
import { useInput } from "ink";
|
|
4
4
|
import { ChatInterface } from "./ChatInterface.js";
|
|
5
|
-
import { ChatProvider } from "../contexts/useChat.js";
|
|
5
|
+
import { ChatProvider, useChat } from "../contexts/useChat.js";
|
|
6
6
|
import { AppProvider } from "../contexts/useAppConfig.js";
|
|
7
7
|
import { WorktreeExitPrompt } from "./WorktreeExitPrompt.js";
|
|
8
8
|
import { hasUncommittedChanges, hasNewCommits, getDefaultRemoteBranch, } from "wave-agent-sdk";
|
|
9
|
-
|
|
9
|
+
/** Wraps ChatInterface with worktree exit handling, using useChat() for hook access. */
|
|
10
|
+
const ChatWithExitPrompt = ({ worktreeSession, onExit }) => {
|
|
11
|
+
const { triggerWorktreeRemoveHook } = useChat();
|
|
10
12
|
const [isExiting, setIsExiting] = useState(false);
|
|
11
13
|
const [worktreeStatus, setWorktreeStatus] = useState(null);
|
|
12
14
|
const handleSignal = useCallback(async () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
setIsExiting(true);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
onExit(true);
|
|
27
|
-
}
|
|
15
|
+
const cwd = worktreeSession.path;
|
|
16
|
+
const baseBranch = getDefaultRemoteBranch(cwd);
|
|
17
|
+
const hasChanges = hasUncommittedChanges(cwd);
|
|
18
|
+
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
19
|
+
if (hasChanges || hasCommits) {
|
|
20
|
+
setWorktreeStatus({
|
|
21
|
+
hasUncommittedChanges: hasChanges,
|
|
22
|
+
hasNewCommits: hasCommits,
|
|
23
|
+
});
|
|
24
|
+
setIsExiting(true);
|
|
28
25
|
}
|
|
29
26
|
else {
|
|
30
|
-
onExit(
|
|
27
|
+
onExit(true);
|
|
31
28
|
}
|
|
32
|
-
}, [worktreeSession,
|
|
29
|
+
}, [worktreeSession, onExit]);
|
|
33
30
|
useInput((input, key) => {
|
|
34
31
|
if (input === "c" && key.ctrl) {
|
|
35
32
|
handleSignal();
|
|
@@ -45,11 +42,28 @@ const AppWithProviders = ({ bypassPermissions, permissionMode, pluginDirs, tools
|
|
|
45
42
|
process.off("SIGTERM", onSigTerm);
|
|
46
43
|
};
|
|
47
44
|
}, [handleSignal]);
|
|
48
|
-
if (isExiting &&
|
|
49
|
-
return (_jsx(WorktreeExitPrompt, { name: worktreeSession.name, path: worktreeSession.path, hasUncommittedChanges: worktreeStatus.hasUncommittedChanges, hasNewCommits: worktreeStatus.hasNewCommits, onKeep: () => onExit(false), onRemove:
|
|
45
|
+
if (isExiting && worktreeStatus) {
|
|
46
|
+
return (_jsx(WorktreeExitPrompt, { name: worktreeSession.name, path: worktreeSession.path, hasUncommittedChanges: worktreeStatus.hasUncommittedChanges, hasNewCommits: worktreeStatus.hasNewCommits, onKeep: () => onExit(false), onRemove: async () => {
|
|
47
|
+
await triggerWorktreeRemoveHook(worktreeSession.path);
|
|
48
|
+
onExit(true);
|
|
49
|
+
}, onCancel: () => setIsExiting(false) }));
|
|
50
|
+
}
|
|
51
|
+
return _jsx(ChatInterface, {});
|
|
52
|
+
};
|
|
53
|
+
const AppWithProviders = ({ bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, mcpServers, onExit, }) => {
|
|
54
|
+
// Handle Ctrl-C for non-worktree sessions (immediate exit)
|
|
55
|
+
// Ink runs terminal in raw mode, so Ctrl+C arrives as useInput event, not SIGINT
|
|
56
|
+
useInput((input, key) => {
|
|
57
|
+
if (!worktreeSession && input === "c" && key.ctrl) {
|
|
58
|
+
onExit(false);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
if (worktreeSession) {
|
|
63
|
+
return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, mcpServers: mcpServers, children: _jsx(ChatWithExitPrompt, { worktreeSession: worktreeSession, onExit: onExit }) }));
|
|
50
64
|
}
|
|
51
|
-
return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, children: _jsx(ChatInterface, {}) }));
|
|
65
|
+
return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, mcpServers: mcpServers, children: _jsx(ChatInterface, {}) }));
|
|
52
66
|
};
|
|
53
|
-
export const App = ({ restoreSessionId, continueLastSession, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, onExit, }) => {
|
|
54
|
-
return (_jsx(AppProvider, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, children: _jsx(AppWithProviders, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, onExit: onExit }) }));
|
|
67
|
+
export const App = ({ restoreSessionId, continueLastSession, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, version, model, mcpServers, onExit, }) => {
|
|
68
|
+
return (_jsx(AppProvider, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, children: _jsx(AppWithProviders, { bypassPermissions: bypassPermissions, permissionMode: permissionMode, pluginDirs: pluginDirs, tools: tools, allowedTools: allowedTools, disallowedTools: disallowedTools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, mcpServers: mcpServers, onExit: onExit }) }));
|
|
55
69
|
};
|
|
@@ -65,6 +65,7 @@ export interface ChatContextType {
|
|
|
65
65
|
version?: string;
|
|
66
66
|
workdir?: string;
|
|
67
67
|
recreateAgent: () => void;
|
|
68
|
+
triggerWorktreeRemoveHook: (worktreePath: string) => Promise<void>;
|
|
68
69
|
}
|
|
69
70
|
export declare const useChat: () => ChatContextType;
|
|
70
71
|
export interface ChatProviderProps extends BaseAppProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -15,7 +15,7 @@ export const useChat = () => {
|
|
|
15
15
|
}
|
|
16
16
|
return context;
|
|
17
17
|
};
|
|
18
|
-
export const ChatProvider = ({ children, bypassPermissions, permissionMode: initialPermissionMode, pluginDirs, tools, allowedTools, disallowedTools, workdir, worktreeSession, version, model, }) => {
|
|
18
|
+
export const ChatProvider = ({ children, bypassPermissions, permissionMode: initialPermissionMode, pluginDirs, tools, allowedTools, disallowedTools, workdir, worktreeSession, version, model, mcpServers, }) => {
|
|
19
19
|
const { restoreSessionId, continueLastSession } = useAppConfig();
|
|
20
20
|
const { stdout } = useStdout();
|
|
21
21
|
// Message Display State
|
|
@@ -50,7 +50,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
50
50
|
const [configuredModels, setConfiguredModels] = useState([]);
|
|
51
51
|
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
52
52
|
// MCP State
|
|
53
|
-
const [
|
|
53
|
+
const [mcpServerStatuses, setMcpServerStatuses] = useState([]);
|
|
54
54
|
// Background tasks state
|
|
55
55
|
const [backgroundTasks, setBackgroundTasks] = useState([]);
|
|
56
56
|
// Tasks state
|
|
@@ -116,8 +116,8 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
116
116
|
onMessagesChange: () => {
|
|
117
117
|
throttledSetMessages();
|
|
118
118
|
},
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
onMcpServersChange: (servers) => {
|
|
120
|
+
setMcpServerStatuses([...servers]);
|
|
121
121
|
},
|
|
122
122
|
onSessionIdChange: (sessionId) => {
|
|
123
123
|
setSessionId(sessionId);
|
|
@@ -181,6 +181,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
181
181
|
worktreeName: worktreeSession?.name,
|
|
182
182
|
isNewWorktree: worktreeSession?.isNew,
|
|
183
183
|
model,
|
|
184
|
+
mcpServers,
|
|
184
185
|
});
|
|
185
186
|
agentRef.current = agent;
|
|
186
187
|
// Get initial state
|
|
@@ -195,8 +196,8 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
195
196
|
setCurrentModelState(agent.getModelConfig().model);
|
|
196
197
|
setConfiguredModels(agent.getConfiguredModels());
|
|
197
198
|
// Get initial MCP servers state
|
|
198
|
-
const
|
|
199
|
-
|
|
199
|
+
const initialMcpServers = agent.getMcpServers?.() || [];
|
|
200
|
+
setMcpServerStatuses(initialMcpServers);
|
|
200
201
|
// Get initial commands
|
|
201
202
|
const agentSlashCommands = agent.getSlashCommands?.() || [];
|
|
202
203
|
setSlashCommands(agentSlashCommands);
|
|
@@ -218,6 +219,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
218
219
|
model,
|
|
219
220
|
initialPermissionMode,
|
|
220
221
|
throttledSetMessages,
|
|
222
|
+
mcpServers,
|
|
221
223
|
]);
|
|
222
224
|
// Recreate agent (e.g. after plugin install) — destroys current agent and reinitializes
|
|
223
225
|
const recreateAgent = useCallback(() => {
|
|
@@ -232,7 +234,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
232
234
|
}
|
|
233
235
|
agentRef.current = null;
|
|
234
236
|
setMessages([]);
|
|
235
|
-
|
|
237
|
+
setMcpServerStatuses([]);
|
|
236
238
|
setSlashCommands([]);
|
|
237
239
|
setSessionId("");
|
|
238
240
|
setIsLoading(false);
|
|
@@ -264,6 +266,10 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
264
266
|
}
|
|
265
267
|
};
|
|
266
268
|
}, []);
|
|
269
|
+
// Trigger WorktreeRemove hook BEFORE agent destruction
|
|
270
|
+
const triggerWorktreeRemoveHook = useCallback(async (worktreePath) => {
|
|
271
|
+
await agentRef.current?.triggerWorktreeRemoveHook(worktreePath);
|
|
272
|
+
}, []);
|
|
267
273
|
// Send message function (including judgment logic)
|
|
268
274
|
const sendMessage = useCallback(async (content, images, longTextMap) => {
|
|
269
275
|
// Check if there's content to send (text content or image attachments)
|
|
@@ -472,7 +478,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
472
478
|
getConfiguredModels,
|
|
473
479
|
setModel,
|
|
474
480
|
isCompacting,
|
|
475
|
-
mcpServers,
|
|
481
|
+
mcpServers: mcpServerStatuses,
|
|
476
482
|
connectMcpServer,
|
|
477
483
|
disconnectMcpServer,
|
|
478
484
|
backgroundTasks,
|
|
@@ -501,6 +507,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
501
507
|
version,
|
|
502
508
|
workdir,
|
|
503
509
|
recreateAgent,
|
|
510
|
+
triggerWorktreeRemoveHook,
|
|
504
511
|
};
|
|
505
512
|
return (_jsx(ChatContext.Provider, { value: contextValue, children: children }));
|
|
506
513
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,wBAAsB,IAAI,kBAqZzB;AAGD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import yargs from "yargs";
|
|
2
2
|
import { hideBin } from "yargs/helpers";
|
|
3
3
|
import { startCli } from "./cli.js";
|
|
4
|
-
import { generateRandomName } from "wave-agent-sdk";
|
|
4
|
+
import { generateRandomName, setCurrentWorktreeSession, } from "wave-agent-sdk";
|
|
5
5
|
import { createWorktree } from "./utils/worktree.js";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { readFileSync } from "fs";
|
|
@@ -87,6 +87,11 @@ export async function main() {
|
|
|
87
87
|
description: "Specify the AI model to use",
|
|
88
88
|
type: "string",
|
|
89
89
|
global: false,
|
|
90
|
+
})
|
|
91
|
+
.option("mcp-config", {
|
|
92
|
+
description: "MCP server configuration as JSON string (same format as .mcp.json)",
|
|
93
|
+
type: "string",
|
|
94
|
+
global: false,
|
|
90
95
|
})
|
|
91
96
|
.option("acp", {
|
|
92
97
|
description: "Run as an ACP bridge",
|
|
@@ -208,6 +213,18 @@ export async function main() {
|
|
|
208
213
|
const tools = parseTools(argv.tools);
|
|
209
214
|
const allowedTools = parseTools(argv.allowedTools);
|
|
210
215
|
const disallowedTools = parseTools(argv.disallowedTools);
|
|
216
|
+
// Parse MCP server configuration from --mcp-config JSON string
|
|
217
|
+
let mcpServers;
|
|
218
|
+
if (argv.mcpConfig) {
|
|
219
|
+
try {
|
|
220
|
+
const parsed = JSON.parse(argv.mcpConfig);
|
|
221
|
+
mcpServers = parsed.mcpServers || parsed;
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
console.error("Failed to parse --mcp-config as JSON");
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
211
228
|
// Resolve plugin directories to absolute paths before any worktree logic
|
|
212
229
|
const pluginDirs = argv.pluginDir?.map((dir) => path.resolve(originalCwd, dir));
|
|
213
230
|
let worktreeSession;
|
|
@@ -219,6 +236,15 @@ export async function main() {
|
|
|
219
236
|
name = generateRandomName();
|
|
220
237
|
}
|
|
221
238
|
worktreeSession = createWorktree(name, originalCwd);
|
|
239
|
+
// Register worktree session so system prompt includes worktree warnings
|
|
240
|
+
setCurrentWorktreeSession({
|
|
241
|
+
originalCwd,
|
|
242
|
+
worktreePath: worktreeSession.path,
|
|
243
|
+
worktreeBranch: worktreeSession.branch,
|
|
244
|
+
worktreeName: worktreeSession.name,
|
|
245
|
+
isNew: worktreeSession.isNew,
|
|
246
|
+
repoRoot: worktreeSession.repoRoot,
|
|
247
|
+
});
|
|
222
248
|
}
|
|
223
249
|
const workdir = worktreeSession?.path || originalCwd;
|
|
224
250
|
if (worktreeSession) {
|
|
@@ -252,6 +278,7 @@ export async function main() {
|
|
|
252
278
|
workdir,
|
|
253
279
|
version,
|
|
254
280
|
model: argv.model,
|
|
281
|
+
mcpServers,
|
|
255
282
|
});
|
|
256
283
|
}
|
|
257
284
|
// Handle print mode directly
|
|
@@ -272,6 +299,7 @@ export async function main() {
|
|
|
272
299
|
workdir,
|
|
273
300
|
version,
|
|
274
301
|
model: argv.model,
|
|
302
|
+
mcpServers,
|
|
275
303
|
});
|
|
276
304
|
}
|
|
277
305
|
await startCli({
|
|
@@ -287,6 +315,7 @@ export async function main() {
|
|
|
287
315
|
workdir,
|
|
288
316
|
version,
|
|
289
317
|
model: argv.model,
|
|
318
|
+
mcpServers,
|
|
290
319
|
});
|
|
291
320
|
}
|
|
292
321
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputHandlers.d.ts","sourceRoot":"","sources":["../../src/managers/inputHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAwB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,EACL,UAAU,EACV,WAAW,EACX,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,0BAA0B,GACrC,MAAM,MAAM,EACZ,aAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAClC,MAcF,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,OAAO,UAAU,EACjB,UAAU,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EACrC,WAAW,OAAO,CAAC,qBAAqB,CAAC,EACzC,yBAAyB,KAAK,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,KACD,OAAO,CAAC,IAAI,CA8Dd,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,UAAU,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,KACpC,OAAO,CAAC,OAAO,CAiBjB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,aAAa,cAAc,EAC3B,UAAU,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EACrC,WAAW,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"inputHandlers.d.ts","sourceRoot":"","sources":["../../src/managers/inputHandlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -74,7 +74,12 @@ export const handlePasteImage = async (dispatch) => {
|
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
export const cyclePermissionMode = (currentMode, dispatch, callbacks) => {
|
|
77
|
-
const modes = [
|
|
77
|
+
const modes = [
|
|
78
|
+
"default",
|
|
79
|
+
"acceptEdits",
|
|
80
|
+
"bypassPermissions",
|
|
81
|
+
"plan",
|
|
82
|
+
];
|
|
78
83
|
const currentIndex = modes.indexOf(currentMode);
|
|
79
84
|
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
|
|
80
85
|
const nextMode = modes[nextIndex];
|
|
@@ -542,8 +542,8 @@ export function inputReducer(state, action) {
|
|
|
542
542
|
const modes = [
|
|
543
543
|
"default",
|
|
544
544
|
"acceptEdits",
|
|
545
|
-
"plan",
|
|
546
545
|
"bypassPermissions",
|
|
546
|
+
"plan",
|
|
547
547
|
];
|
|
548
548
|
const currentIndex = modes.indexOf(state.permissionMode);
|
|
549
549
|
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % modes.length;
|
package/dist/print-cli.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface PrintCliOptions extends BaseAppProps {
|
|
|
4
4
|
continueLastSession?: boolean;
|
|
5
5
|
message?: string;
|
|
6
6
|
showStats?: boolean;
|
|
7
|
+
/** MCP server config parsed from --mcp-config CLI argument */
|
|
8
|
+
mcpServers?: Record<string, import("wave-agent-sdk").McpServerConfig>;
|
|
7
9
|
}
|
|
8
10
|
export declare function startPrintCli(options: PrintCliOptions): Promise<void>;
|
|
9
11
|
//# sourceMappingURL=print-cli.d.ts.map
|
package/dist/print-cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"print-cli.d.ts","sourceRoot":"","sources":["../src/print-cli.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,gBAAgB,EAAE,eAAe,CAAC,CAAC;CACvE;AAgBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuN3E"}
|
package/dist/print-cli.js
CHANGED
|
@@ -14,7 +14,7 @@ function displayTimingInfo(startTime, showStats) {
|
|
|
14
14
|
}
|
|
15
15
|
export async function startPrintCli(options) {
|
|
16
16
|
const startTime = new Date();
|
|
17
|
-
const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, model, } = options;
|
|
17
|
+
const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, permissionMode, pluginDirs, tools, allowedTools, disallowedTools, worktreeSession, workdir, model, mcpServers, } = options;
|
|
18
18
|
if ((typeof message !== "string" || message.trim() === "") &&
|
|
19
19
|
!continueLastSession &&
|
|
20
20
|
!restoreSessionId) {
|
|
@@ -110,6 +110,7 @@ export async function startPrintCli(options) {
|
|
|
110
110
|
disallowedTools,
|
|
111
111
|
workdir,
|
|
112
112
|
model,
|
|
113
|
+
mcpServers,
|
|
113
114
|
// 保持流式模式以获得更好的命令行用户体验
|
|
114
115
|
});
|
|
115
116
|
// Send message if provided and not empty
|
package/dist/types.d.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
|
export interface BaseAppProps {
|
|
4
4
|
bypassPermissions?: boolean;
|
|
5
5
|
permissionMode?: PermissionMode;
|
|
@@ -11,5 +11,7 @@ export interface BaseAppProps {
|
|
|
11
11
|
workdir?: string;
|
|
12
12
|
version?: string;
|
|
13
13
|
model?: string;
|
|
14
|
+
/** Optional MCP server configs to pass to Agent.create() */
|
|
15
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
14
16
|
}
|
|
15
17
|
//# sourceMappingURL=types.d.ts.map
|
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.3",
|
|
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.3"
|
|
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 (
|
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
|
|
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
|
|
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
|
}
|