@sudocode-ai/claude-code-acp 0.12.10 → 0.13.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/README.md +3 -9
- package/dist/acp-agent.d.ts +194 -0
- package/dist/acp-agent.d.ts.map +1 -0
- package/dist/acp-agent.js +48 -25
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib.d.ts +7 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/mcp-server.d.ts +21 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +214 -158
- package/dist/settings.d.ts +123 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/tools.d.ts +50 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +3 -1
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +22 -12
- package/dist/tests/acp-agent-fork.test.js +0 -338
- package/dist/tests/acp-agent.test.js +0 -753
- package/dist/tests/extract-lines.test.js +0 -79
- package/dist/tests/fork-session.test.js +0 -83
- package/dist/tests/replace-and-calculate-location.test.js +0 -266
- package/dist/tests/settings.test.js +0 -462
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Use [Claude Code](https://www.anthropic.com/claude-code) from [ACP-compatible](https://agentclientprotocol.com) clients such as [Zed](https://zed.dev)!
|
|
6
6
|
|
|
7
|
-
This tool implements an ACP agent by using the official [Claude
|
|
7
|
+
This tool implements an ACP agent by using the official [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), supporting:
|
|
8
8
|
|
|
9
9
|
- Context @-mentions
|
|
10
10
|
- Images
|
|
@@ -32,20 +32,14 @@ Read the docs on [External Agent](https://zed.dev/docs/ai/external-agents) suppo
|
|
|
32
32
|
|
|
33
33
|
### Other clients
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
- [marimo notebook](https://github.com/marimo-team/marimo)
|
|
37
|
-
- Neovim
|
|
38
|
-
- via [CodeCompanion.nvim](https://codecompanion.olimorris.dev/configuration/adapters#setup-claude-code-via-acp)
|
|
39
|
-
- via [yetone/avante.nvim](https://github.com/yetone/avante.nvim)
|
|
40
|
-
|
|
41
|
-
[Submit a PR](https://github.com/zed-industries/claude-code-acp/pulls) to add yours!
|
|
35
|
+
Or try it with any of the other [ACP compatible clients](https://agentclientprotocol.com/overview/clients)!
|
|
42
36
|
|
|
43
37
|
#### Installation
|
|
44
38
|
|
|
45
39
|
Install the adapter from `npm`:
|
|
46
40
|
|
|
47
41
|
```bash
|
|
48
|
-
npm install @zed-industries/claude-code-acp
|
|
42
|
+
npm install -g @zed-industries/claude-code-acp
|
|
49
43
|
```
|
|
50
44
|
|
|
51
45
|
You can then use `claude-code-acp` as a regular ACP agent:
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Agent, AgentSideConnection, AuthenticateRequest, CancelNotification, ClientCapabilities, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, LoadSessionRequest, LoadSessionResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, SessionNotification, SetSessionModelRequest, SetSessionModelResponse, SetSessionModeRequest, SetSessionModeResponse, TerminalHandle, TerminalOutputResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { SettingsManager } from "./settings.js";
|
|
3
|
+
import { CanUseTool, Options, PermissionMode, Query, SDKPartialAssistantMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
4
|
+
import { Pushable } from "./utils.js";
|
|
5
|
+
import { ContentBlockParam } from "@anthropic-ai/sdk/resources";
|
|
6
|
+
import { BetaContentBlock, BetaRawContentBlockDelta } from "@anthropic-ai/sdk/resources/beta.mjs";
|
|
7
|
+
export declare const CLAUDE_CONFIG_DIR: string;
|
|
8
|
+
/**
|
|
9
|
+
* Logger interface for customizing logging output
|
|
10
|
+
*/
|
|
11
|
+
export interface Logger {
|
|
12
|
+
log: (...args: any[]) => void;
|
|
13
|
+
error: (...args: any[]) => void;
|
|
14
|
+
}
|
|
15
|
+
type Session = {
|
|
16
|
+
query: Query;
|
|
17
|
+
input: Pushable<SDKUserMessage>;
|
|
18
|
+
cancelled: boolean;
|
|
19
|
+
permissionMode: PermissionMode;
|
|
20
|
+
settingsManager: SettingsManager;
|
|
21
|
+
abortController: AbortController;
|
|
22
|
+
cwd: string;
|
|
23
|
+
/** Optional: the actual session file path (for forked sessions where filename differs from sessionId) */
|
|
24
|
+
sessionFilePath?: string;
|
|
25
|
+
};
|
|
26
|
+
type BackgroundTerminal = {
|
|
27
|
+
handle: TerminalHandle;
|
|
28
|
+
status: "started";
|
|
29
|
+
lastOutput: TerminalOutputResponse | null;
|
|
30
|
+
} | {
|
|
31
|
+
status: "aborted" | "exited" | "killed" | "timedOut";
|
|
32
|
+
pendingOutput: TerminalOutputResponse;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Extra metadata that can be given to Claude Code when creating a new session.
|
|
36
|
+
*/
|
|
37
|
+
export type NewSessionMeta = {
|
|
38
|
+
claudeCode?: {
|
|
39
|
+
/**
|
|
40
|
+
* Options forwarded to Claude Code when starting a new session.
|
|
41
|
+
* Those parameters will be ignored and managed by ACP:
|
|
42
|
+
* - cwd
|
|
43
|
+
* - includePartialMessages
|
|
44
|
+
* - allowDangerouslySkipPermissions
|
|
45
|
+
* - permissionMode
|
|
46
|
+
* - canUseTool
|
|
47
|
+
* - executable
|
|
48
|
+
* Those parameters will be used and updated to work with ACP:
|
|
49
|
+
* - hooks (merged with ACP's hooks)
|
|
50
|
+
* - mcpServers (merged with ACP's mcpServers)
|
|
51
|
+
*/
|
|
52
|
+
options?: Options;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Extra metadata that the agent provides for each tool_call / tool_update update.
|
|
57
|
+
*/
|
|
58
|
+
export type ToolUpdateMeta = {
|
|
59
|
+
claudeCode?: {
|
|
60
|
+
toolName: string;
|
|
61
|
+
toolResponse?: unknown;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
type ToolUseCache = {
|
|
65
|
+
[key: string]: {
|
|
66
|
+
type: "tool_use" | "server_tool_use" | "mcp_tool_use";
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
input: any;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export declare class ClaudeAcpAgent implements Agent {
|
|
73
|
+
sessions: {
|
|
74
|
+
[key: string]: Session;
|
|
75
|
+
};
|
|
76
|
+
client: AgentSideConnection;
|
|
77
|
+
toolUseCache: ToolUseCache;
|
|
78
|
+
backgroundTerminals: {
|
|
79
|
+
[key: string]: BackgroundTerminal;
|
|
80
|
+
};
|
|
81
|
+
clientCapabilities?: ClientCapabilities;
|
|
82
|
+
logger: Logger;
|
|
83
|
+
constructor(client: AgentSideConnection, logger?: Logger);
|
|
84
|
+
initialize(request: InitializeRequest): Promise<InitializeResponse>;
|
|
85
|
+
newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
|
|
86
|
+
/**
|
|
87
|
+
* Fork an existing session to create a new independent session.
|
|
88
|
+
* This is the ACP protocol method handler for session/fork.
|
|
89
|
+
* Named unstable_forkSession to match SDK expectations (session/fork routes to this method).
|
|
90
|
+
*/
|
|
91
|
+
unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
|
|
92
|
+
/**
|
|
93
|
+
* Get the directory where session files are stored for a given cwd.
|
|
94
|
+
*/
|
|
95
|
+
private getSessionDirPath;
|
|
96
|
+
/**
|
|
97
|
+
* Extract the internal sessionId from a session JSONL file.
|
|
98
|
+
* The CLI stores the actual session ID inside the file, which may differ from the filename.
|
|
99
|
+
* For forked sessions, the filename is "agent-xxx" but the internal sessionId is a UUID.
|
|
100
|
+
*/
|
|
101
|
+
private extractInternalSessionId;
|
|
102
|
+
/**
|
|
103
|
+
* Promote a sidechain session to a regular session by modifying the session file.
|
|
104
|
+
* Forked sessions have "isSidechain": true which prevents them from being resumed.
|
|
105
|
+
* This method changes it to false so the session can be resumed/forked again.
|
|
106
|
+
*/
|
|
107
|
+
private promoteToFullSession;
|
|
108
|
+
/**
|
|
109
|
+
* Update the sessionId in all lines of a session JSONL file.
|
|
110
|
+
* This is used when we need to assign a new unique session ID to avoid collisions.
|
|
111
|
+
*/
|
|
112
|
+
private updateSessionIdInFile;
|
|
113
|
+
/**
|
|
114
|
+
* Discover the CLI-assigned session ID by looking for new session files.
|
|
115
|
+
* Returns the CLI's session ID if found, or the original sessionId if not.
|
|
116
|
+
*/
|
|
117
|
+
private discoverCliSessionId;
|
|
118
|
+
/**
|
|
119
|
+
* Alias for unstable_forkSession for convenience.
|
|
120
|
+
*/
|
|
121
|
+
forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
|
|
122
|
+
/**
|
|
123
|
+
* Load an existing session to resume a previous conversation.
|
|
124
|
+
* This is the ACP protocol method handler for session/load.
|
|
125
|
+
*/
|
|
126
|
+
loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use loadSession instead. This is kept for backward compatibility.
|
|
129
|
+
*/
|
|
130
|
+
unstable_resumeSession(params: {
|
|
131
|
+
sessionId: string;
|
|
132
|
+
_meta?: {
|
|
133
|
+
cwd?: string;
|
|
134
|
+
mcpServers?: any[];
|
|
135
|
+
[key: string]: unknown;
|
|
136
|
+
} | null;
|
|
137
|
+
}): Promise<LoadSessionResponse>;
|
|
138
|
+
authenticate(_params: AuthenticateRequest): Promise<void>;
|
|
139
|
+
prompt(params: PromptRequest): Promise<PromptResponse>;
|
|
140
|
+
cancel(params: CancelNotification): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Handle extension methods from the client.
|
|
143
|
+
*
|
|
144
|
+
* Currently supports:
|
|
145
|
+
* - `_session/flush`: Flush a session to disk for fork-with-flush support
|
|
146
|
+
*/
|
|
147
|
+
extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
148
|
+
/**
|
|
149
|
+
* Flush a session to disk by aborting its query subprocess.
|
|
150
|
+
*
|
|
151
|
+
* This is used by the fork-with-flush mechanism to ensure session data
|
|
152
|
+
* is persisted to disk before forking. When the Claude SDK subprocess
|
|
153
|
+
* exits (via abort), it writes the session data to:
|
|
154
|
+
* ~/.claude/projects/<cwd-hash>/<sessionId>.jsonl
|
|
155
|
+
*
|
|
156
|
+
* After this method completes, the session is removed from memory and
|
|
157
|
+
* must be reloaded via loadSession() to continue using it.
|
|
158
|
+
*/
|
|
159
|
+
private handleSessionFlush;
|
|
160
|
+
/**
|
|
161
|
+
* Get the file path where Claude Code stores session data.
|
|
162
|
+
*
|
|
163
|
+
* Claude Code stores sessions at:
|
|
164
|
+
* ~/.claude/projects/<cwd-hash>/<sessionId>.jsonl
|
|
165
|
+
*
|
|
166
|
+
* Where <cwd-hash> is the cwd with `/` replaced by `-`
|
|
167
|
+
* Note: We resolve the real path to handle macOS symlinks like /var -> /private/var
|
|
168
|
+
*/
|
|
169
|
+
private getSessionFilePath;
|
|
170
|
+
/**
|
|
171
|
+
* Wait for a session file to appear on disk.
|
|
172
|
+
*
|
|
173
|
+
* @param filePath - Path to the session file
|
|
174
|
+
* @param timeout - Maximum time to wait in milliseconds
|
|
175
|
+
* @returns true if file appears, false if timeout
|
|
176
|
+
*/
|
|
177
|
+
private waitForSessionFile;
|
|
178
|
+
unstable_setSessionModel(params: SetSessionModelRequest): Promise<SetSessionModelResponse | void>;
|
|
179
|
+
setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
|
|
180
|
+
readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
|
|
181
|
+
writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
|
|
182
|
+
canUseTool(sessionId: string): CanUseTool;
|
|
183
|
+
private createSession;
|
|
184
|
+
}
|
|
185
|
+
export declare function promptToClaude(prompt: PromptRequest): SDKUserMessage;
|
|
186
|
+
/**
|
|
187
|
+
* Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
|
|
188
|
+
* Only handles text, image, and thinking chunks for now.
|
|
189
|
+
*/
|
|
190
|
+
export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger): SessionNotification[];
|
|
191
|
+
export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger): SessionNotification[];
|
|
192
|
+
export declare function runAcp(): void;
|
|
193
|
+
export {};
|
|
194
|
+
//# sourceMappingURL=acp-agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"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"}
|
package/dist/acp-agent.js
CHANGED
|
@@ -54,6 +54,7 @@ export class ClaudeAcpAgent {
|
|
|
54
54
|
},
|
|
55
55
|
sessionCapabilities: {
|
|
56
56
|
fork: {},
|
|
57
|
+
resume: {},
|
|
57
58
|
},
|
|
58
59
|
loadSession: true,
|
|
59
60
|
},
|
|
@@ -84,7 +85,7 @@ export class ClaudeAcpAgent {
|
|
|
84
85
|
// Get the session directory to track new files
|
|
85
86
|
const sessionDir = this.getSessionDirPath(params.cwd);
|
|
86
87
|
const beforeFiles = new Set(fs.existsSync(sessionDir)
|
|
87
|
-
? fs.readdirSync(sessionDir).filter(f => f.endsWith(
|
|
88
|
+
? fs.readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl"))
|
|
88
89
|
: []);
|
|
89
90
|
const result = await this.createSession({
|
|
90
91
|
cwd: params.cwd,
|
|
@@ -95,7 +96,7 @@ export class ClaudeAcpAgent {
|
|
|
95
96
|
forkSession: true,
|
|
96
97
|
});
|
|
97
98
|
// Wait briefly for CLI to create the session file
|
|
98
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
99
100
|
// Find the CLI-assigned session ID by looking for new session files
|
|
100
101
|
const cliSessionId = await this.discoverCliSessionId(sessionDir, beforeFiles, result.sessionId);
|
|
101
102
|
if (cliSessionId && cliSessionId !== result.sessionId) {
|
|
@@ -167,13 +168,13 @@ export class ClaudeAcpAgent {
|
|
|
167
168
|
if (!fs.existsSync(filePath)) {
|
|
168
169
|
return null;
|
|
169
170
|
}
|
|
170
|
-
const content = fs.readFileSync(filePath,
|
|
171
|
-
const firstLine = content.split(
|
|
171
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
172
|
+
const firstLine = content.split("\n").find((line) => line.trim().length > 0);
|
|
172
173
|
if (!firstLine) {
|
|
173
174
|
return null;
|
|
174
175
|
}
|
|
175
176
|
const parsed = JSON.parse(firstLine);
|
|
176
|
-
if (parsed.sessionId && typeof parsed.sessionId ===
|
|
177
|
+
if (parsed.sessionId && typeof parsed.sessionId === "string") {
|
|
177
178
|
// Verify it's a UUID format
|
|
178
179
|
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(parsed.sessionId);
|
|
179
180
|
if (isUuid) {
|
|
@@ -197,8 +198,8 @@ export class ClaudeAcpAgent {
|
|
|
197
198
|
if (!fs.existsSync(filePath)) {
|
|
198
199
|
return false;
|
|
199
200
|
}
|
|
200
|
-
const content = fs.readFileSync(filePath,
|
|
201
|
-
const lines = content.split(
|
|
201
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
202
|
+
const lines = content.split("\n");
|
|
202
203
|
const modifiedLines = [];
|
|
203
204
|
for (const line of lines) {
|
|
204
205
|
if (!line.trim()) {
|
|
@@ -218,7 +219,7 @@ export class ClaudeAcpAgent {
|
|
|
218
219
|
modifiedLines.push(line);
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
|
-
fs.writeFileSync(filePath, modifiedLines.join(
|
|
222
|
+
fs.writeFileSync(filePath, modifiedLines.join("\n"), "utf-8");
|
|
222
223
|
this.logger.log(`[claude-code-acp] Promoted sidechain to full session: ${filePath}`);
|
|
223
224
|
return true;
|
|
224
225
|
}
|
|
@@ -236,8 +237,8 @@ export class ClaudeAcpAgent {
|
|
|
236
237
|
if (!fs.existsSync(filePath)) {
|
|
237
238
|
return false;
|
|
238
239
|
}
|
|
239
|
-
const content = fs.readFileSync(filePath,
|
|
240
|
-
const lines = content.split(
|
|
240
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
241
|
+
const lines = content.split("\n");
|
|
241
242
|
const modifiedLines = [];
|
|
242
243
|
for (const line of lines) {
|
|
243
244
|
if (!line.trim()) {
|
|
@@ -247,7 +248,7 @@ export class ClaudeAcpAgent {
|
|
|
247
248
|
try {
|
|
248
249
|
const parsed = JSON.parse(line);
|
|
249
250
|
// Update the sessionId in each line
|
|
250
|
-
if (parsed.sessionId && typeof parsed.sessionId ===
|
|
251
|
+
if (parsed.sessionId && typeof parsed.sessionId === "string") {
|
|
251
252
|
parsed.sessionId = newSessionId;
|
|
252
253
|
}
|
|
253
254
|
modifiedLines.push(JSON.stringify(parsed));
|
|
@@ -257,7 +258,7 @@ export class ClaudeAcpAgent {
|
|
|
257
258
|
modifiedLines.push(line);
|
|
258
259
|
}
|
|
259
260
|
}
|
|
260
|
-
fs.writeFileSync(filePath, modifiedLines.join(
|
|
261
|
+
fs.writeFileSync(filePath, modifiedLines.join("\n"), "utf-8");
|
|
261
262
|
this.logger.log(`[claude-code-acp] Updated session ID in file: ${filePath}`);
|
|
262
263
|
return true;
|
|
263
264
|
}
|
|
@@ -276,18 +277,18 @@ export class ClaudeAcpAgent {
|
|
|
276
277
|
const agentPattern = /^agent-[a-f0-9]+\.jsonl$/;
|
|
277
278
|
while (Date.now() - start < timeout) {
|
|
278
279
|
if (fs.existsSync(sessionDir)) {
|
|
279
|
-
const currentFiles = fs.readdirSync(sessionDir).filter(f => f.endsWith(
|
|
280
|
+
const currentFiles = fs.readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl"));
|
|
280
281
|
// Only look for new files that match the agent-xxx pattern
|
|
281
282
|
// This prevents picking up renamed UUID files from previous forks
|
|
282
|
-
const newFiles = currentFiles.filter(f => !beforeFiles.has(f) && agentPattern.test(f));
|
|
283
|
+
const newFiles = currentFiles.filter((f) => !beforeFiles.has(f) && agentPattern.test(f));
|
|
283
284
|
if (newFiles.length === 1) {
|
|
284
285
|
// Found exactly one new agent session file - this is our fork
|
|
285
286
|
this.logger.log(`[claude-code-acp] Discovered fork session file: ${newFiles[0]}`);
|
|
286
|
-
return newFiles[0].replace(
|
|
287
|
+
return newFiles[0].replace(".jsonl", "");
|
|
287
288
|
}
|
|
288
289
|
else if (newFiles.length > 1) {
|
|
289
290
|
// Multiple new agent files - try to find the most recent one
|
|
290
|
-
let newestFile =
|
|
291
|
+
let newestFile = "";
|
|
291
292
|
let newestMtime = 0;
|
|
292
293
|
for (const file of newFiles) {
|
|
293
294
|
const filePath = path.join(sessionDir, file);
|
|
@@ -299,11 +300,11 @@ export class ClaudeAcpAgent {
|
|
|
299
300
|
}
|
|
300
301
|
if (newestFile) {
|
|
301
302
|
this.logger.log(`[claude-code-acp] Discovered fork session file (newest of ${newFiles.length}): ${newestFile}`);
|
|
302
|
-
return newestFile.replace(
|
|
303
|
+
return newestFile.replace(".jsonl", "");
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
}
|
|
306
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
307
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
307
308
|
}
|
|
308
309
|
// Timeout - return fallback
|
|
309
310
|
this.logger.log(`[claude-code-acp] Could not discover CLI session ID, using fallback: ${fallbackId}`);
|
|
@@ -568,7 +569,7 @@ export class ClaudeAcpAgent {
|
|
|
568
569
|
if (fs.existsSync(filePath)) {
|
|
569
570
|
return true;
|
|
570
571
|
}
|
|
571
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
572
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
572
573
|
}
|
|
573
574
|
return false;
|
|
574
575
|
}
|
|
@@ -728,9 +729,18 @@ export class ClaudeAcpAgent {
|
|
|
728
729
|
};
|
|
729
730
|
}
|
|
730
731
|
async createSession(params, creationOpts = {}) {
|
|
731
|
-
//
|
|
732
|
-
//
|
|
733
|
-
|
|
732
|
+
// We want to create a new session id unless it is resume,
|
|
733
|
+
// but not resume + forkSession.
|
|
734
|
+
let sessionId;
|
|
735
|
+
if (creationOpts.forkSession) {
|
|
736
|
+
sessionId = randomUUID();
|
|
737
|
+
}
|
|
738
|
+
else if (creationOpts.resume) {
|
|
739
|
+
sessionId = creationOpts.resume;
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
sessionId = randomUUID();
|
|
743
|
+
}
|
|
734
744
|
const input = new Pushable();
|
|
735
745
|
const settingsManager = new SettingsManager(params.cwd, {
|
|
736
746
|
logger: this.logger,
|
|
@@ -785,16 +795,21 @@ export class ClaudeAcpAgent {
|
|
|
785
795
|
// Extract options from _meta if provided
|
|
786
796
|
const userProvidedOptions = params._meta?.claudeCode?.options;
|
|
787
797
|
const extraArgs = { ...userProvidedOptions?.extraArgs };
|
|
788
|
-
if (creationOpts?.resume === undefined) {
|
|
798
|
+
if (creationOpts?.resume === undefined || creationOpts?.forkSession) {
|
|
789
799
|
// Set our own session id if not resuming an existing session.
|
|
790
800
|
// Note: For forked sessions (resume + fork), Claude CLI assigns its own session ID
|
|
791
801
|
// which means chain forking (fork of a fork) is not currently supported.
|
|
792
802
|
extraArgs["session-id"] = sessionId;
|
|
793
803
|
}
|
|
804
|
+
// Configure thinking tokens from environment variable
|
|
805
|
+
const maxThinkingTokens = process.env.MAX_THINKING_TOKENS
|
|
806
|
+
? parseInt(process.env.MAX_THINKING_TOKENS, 10)
|
|
807
|
+
: undefined;
|
|
794
808
|
const options = {
|
|
795
809
|
systemPrompt,
|
|
796
810
|
settingSources: ["user", "project", "local"],
|
|
797
811
|
stderr: (err) => this.logger.error(err),
|
|
812
|
+
...(maxThinkingTokens !== undefined && { maxThinkingTokens }),
|
|
798
813
|
...userProvidedOptions,
|
|
799
814
|
// Override certain fields that must be controlled by ACP
|
|
800
815
|
cwd: params.cwd,
|
|
@@ -812,6 +827,7 @@ export class ClaudeAcpAgent {
|
|
|
812
827
|
...(process.env.CLAUDE_CODE_EXECUTABLE && {
|
|
813
828
|
pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE,
|
|
814
829
|
}),
|
|
830
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
815
831
|
hooks: {
|
|
816
832
|
...userProvidedOptions?.hooks,
|
|
817
833
|
PreToolUse: [
|
|
@@ -830,7 +846,8 @@ export class ClaudeAcpAgent {
|
|
|
830
846
|
...creationOpts,
|
|
831
847
|
};
|
|
832
848
|
const allowedTools = [];
|
|
833
|
-
|
|
849
|
+
// Disable this for now, not a great way to expose this over ACP at the moment (in progress work so we can revisit)
|
|
850
|
+
const disallowedTools = ["AskUserQuestion"];
|
|
834
851
|
// Check if built-in tools should be disabled
|
|
835
852
|
const disableBuiltInTools = params._meta?.disableBuiltInTools === true;
|
|
836
853
|
if (!disableBuiltInTools) {
|
|
@@ -958,7 +975,13 @@ async function getAvailableSlashCommands(query) {
|
|
|
958
975
|
const commands = await query.supportedCommands();
|
|
959
976
|
return commands
|
|
960
977
|
.map((command) => {
|
|
961
|
-
const input = command.argumentHint
|
|
978
|
+
const input = command.argumentHint
|
|
979
|
+
? {
|
|
980
|
+
hint: Array.isArray(command.argumentHint)
|
|
981
|
+
? command.argumentHint.join(" ")
|
|
982
|
+
: command.argumentHint,
|
|
983
|
+
}
|
|
984
|
+
: null;
|
|
962
985
|
let name = command.name;
|
|
963
986
|
if (command.name.endsWith(" (MCP)")) {
|
|
964
987
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/lib.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ClaudeAcpAgent, runAcp, toAcpNotifications, streamEventToAcpNotifications, type ToolUpdateMeta, type NewSessionMeta, } from "./acp-agent.js";
|
|
2
|
+
export { loadManagedSettings, applyEnvironmentSettings, nodeToWebReadable, nodeToWebWritable, Pushable, unreachable, } from "./utils.js";
|
|
3
|
+
export { createMcpServer } from "./mcp-server.js";
|
|
4
|
+
export { toolInfoFromToolUse, planEntries, toolUpdateFromToolResult, createPreToolUseHook, acpToolNames as toolNames, } from "./tools.js";
|
|
5
|
+
export { SettingsManager, type ClaudeCodeSettings, type PermissionSettings, type PermissionDecision, type PermissionCheckResult, type SettingsManagerOptions, } from "./settings.js";
|
|
6
|
+
export type { ClaudePlanEntry } from "./tools.js";
|
|
7
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,MAAM,EACN,kBAAkB,EAClB,6BAA6B,EAC7B,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,EACR,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,IAAI,SAAS,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ClaudeAcpAgent } from "./acp-agent.js";
|
|
3
|
+
import { ClientCapabilities } from "@agentclientprotocol/sdk";
|
|
4
|
+
export declare const SYSTEM_REMINDER = "\n\n<system-reminder>\nWhenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.\n</system-reminder>";
|
|
5
|
+
export declare function createMcpServer(agent: ClaudeAcpAgent, sessionId: string, clientCapabilities: ClientCapabilities | undefined): McpServer;
|
|
6
|
+
/**
|
|
7
|
+
* Replace text in a file and calculate the line numbers where the edits occurred.
|
|
8
|
+
*
|
|
9
|
+
* @param fileContent - The full file content
|
|
10
|
+
* @param edits - Array of edit operations to apply sequentially
|
|
11
|
+
* @returns the new content and the line numbers where replacements occurred in the final content
|
|
12
|
+
*/
|
|
13
|
+
export declare function replaceAndCalculateLocation(fileContent: string, edits: Array<{
|
|
14
|
+
oldText: string;
|
|
15
|
+
newText: string;
|
|
16
|
+
replaceAll?: boolean;
|
|
17
|
+
}>): {
|
|
18
|
+
newContent: string;
|
|
19
|
+
lineNumbers: number[];
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=mcp-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASpE,OAAO,EAAqB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EACL,kBAAkB,EAGnB,MAAM,0BAA0B,CAAC;AAQlC,eAAO,MAAM,eAAe,iSAIT,CAAC;AA2BpB,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,GACjD,SAAS,CA2nBX;AA+DD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,KAAK,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,GACD;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAyF/C"}
|