claude-code-acp-ts 0.13.1 → 0.16.0

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.
@@ -1,4 +1,4 @@
1
- import { Agent, AgentSideConnection, AuthenticateRequest, CancelNotification, ClientCapabilities, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, ResumeSessionRequest, ResumeSessionResponse, SessionNotification, SetSessionModelRequest, SetSessionModelResponse, SetSessionModeRequest, SetSessionModeResponse, TerminalHandle, TerminalOutputResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
1
+ import { Agent, AgentSideConnection, AuthenticateRequest, CancelNotification, ClientCapabilities, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, LoadSessionRequest, LoadSessionResponse, ListSessionsRequest, ListSessionsResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, ResumeSessionRequest, ResumeSessionResponse, SessionNotification, SetSessionModelRequest, SetSessionModelResponse, SetSessionModeRequest, SetSessionModeResponse, TerminalHandle, TerminalOutputResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
2
2
  import { SettingsManager } from "./settings.js";
3
3
  import { CanUseTool, Options, PermissionMode, Query, SDKPartialAssistantMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
4
4
  import { Pushable } from "./utils.js";
@@ -44,6 +44,7 @@ export type NewSessionMeta = {
44
44
  * Those parameters will be used and updated to work with ACP:
45
45
  * - hooks (merged with ACP's hooks)
46
46
  * - mcpServers (merged with ACP's mcpServers)
47
+ * - disallowedTools (merged with ACP's disallowedTools)
47
48
  */
48
49
  options?: Options;
49
50
  };
@@ -57,12 +58,12 @@ export type ToolUpdateMeta = {
57
58
  toolResponse?: unknown;
58
59
  };
59
60
  };
60
- type ToolUseCache = {
61
+ export type ToolUseCache = {
61
62
  [key: string]: {
62
63
  type: "tool_use" | "server_tool_use" | "mcp_tool_use";
63
64
  id: string;
64
65
  name: string;
65
- input: any;
66
+ input: unknown;
66
67
  };
67
68
  };
68
69
  export declare class ClaudeAcpAgent implements Agent {
@@ -81,11 +82,19 @@ export declare class ClaudeAcpAgent implements Agent {
81
82
  newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
82
83
  unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
83
84
  unstable_resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse>;
85
+ loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
86
+ /**
87
+ * List Claude Code sessions by parsing JSONL files
88
+ * Sessions are stored in ~/.claude/projects/<path-encoded>/
89
+ * Implements the draft session/list RFD spec
90
+ */
91
+ unstable_listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse>;
84
92
  authenticate(_params: AuthenticateRequest): Promise<void>;
85
93
  prompt(params: PromptRequest): Promise<PromptResponse>;
86
94
  cancel(params: CancelNotification): Promise<void>;
87
95
  unstable_setSessionModel(params: SetSessionModelRequest): Promise<SetSessionModelResponse | void>;
88
96
  setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
97
+ private replaySessionHistory;
89
98
  readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
90
99
  writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
91
100
  canUseTool(sessionId: string): CanUseTool;
@@ -96,7 +105,9 @@ export declare function promptToClaude(prompt: PromptRequest): SDKUserMessage;
96
105
  * Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
97
106
  * Only handles text, image, and thinking chunks for now.
98
107
  */
99
- export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger): SessionNotification[];
108
+ export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger, options?: {
109
+ registerHooks?: boolean;
110
+ }): SessionNotification[];
100
111
  export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger): SessionNotification[];
101
112
  export declare function runAcp(): void;
102
113
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAElB,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EAEpB,oBAAoB,EACpB,qBAAqB,EAErB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,UAAU,EAEV,OAAO,EACP,cAAc,EACd,KAAK,EAEL,0BAA0B,EAC1B,cAAc,EACf,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EAAwC,QAAQ,EAAe,MAAM,YAAY,CAAC;AAYzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAIlG,eAAO,MAAM,iBAAiB,QAA2D,CAAC;AAoD1F;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;CACjC;AAED,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;CAClC,CAAC;AAEF,KAAK,kBAAkB,GACnB;IACA,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAC3C,GACC;IACA,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IACrD,aAAa,EAAE,sBAAsB,CAAC;CACvC,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE;QACX;;;;;;;;;;;;WAYG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE;QAEX,QAAQ,EAAE,MAAM,CAAC;QAEjB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG;QACb,IAAI,EAAE,UAAU,GAAG,iBAAiB,GAAG,cAAc,CAAC;QACtD,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,GAAG,CAAC;KACZ,CAAC;CACH,CAAC;AAMF,qBAAa,cAAe,YAAW,KAAK;IAC1C,QAAQ,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,EAAE,mBAAmB,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,mBAAmB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAA;KAAE,CAAM;IAChE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM;IAOlD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgDnE,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAclE,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAc9E,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAepF,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAgKtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,wBAAwB,CAC5B,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAOpC,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA0B9E,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAKxE,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAKjF,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU;YAgI3B,aAAa;CAoT5B;AAqFD,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CA6EpE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,GAAG,wBAAwB,EAAE,EACvF,IAAI,EAAE,WAAW,GAAG,MAAM,EAC1B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,MAAM,GACb,mBAAmB,EAAE,CAqKvB;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,0BAA0B,EACnC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,MAAM,GACb,mBAAmB,EAAE,CAgCvB;AAED,wBAAgB,MAAM,SAMrB"}
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
@@ -3,14 +3,31 @@ import { SettingsManager } from "./settings.js";
3
3
  import { query, } from "@anthropic-ai/claude-agent-sdk";
4
4
  import * as fs from "node:fs";
5
5
  import * as path from "node:path";
6
+ import * as readline from "node:readline";
6
7
  import * as os from "node:os";
7
- import { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./utils.js";
8
+ import { encodeProjectPath, nodeToWebReadable, nodeToWebWritable, Pushable, unreachable, } from "./utils.js";
8
9
  import { createMcpServer } from "./mcp-server.js";
9
10
  import { EDIT_TOOL_NAMES, acpToolNames } from "./tools.js";
10
11
  import { toolInfoFromToolUse, planEntries, toolUpdateFromToolResult, registerHookCallback, createPostToolUseHook, createPreToolUseHook, } from "./tools.js";
11
12
  import packageJson from "../package.json" with { type: "json" };
12
13
  import { randomUUID } from "node:crypto";
13
- export const CLAUDE_CONFIG_DIR = process.env.CLAUDE ?? path.join(os.homedir(), ".claude");
14
+ import { fileURLToPath } from "node:url";
15
+ export const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
16
+ function sessionFilePath(cwd, sessionId) {
17
+ return path.join(CLAUDE_CONFIG_DIR, "projects", encodeProjectPath(cwd), `${sessionId}.jsonl`);
18
+ }
19
+ const MAX_TITLE_LENGTH = 128;
20
+ function sanitizeTitle(text) {
21
+ // Replace newlines and collapse whitespace
22
+ const sanitized = text
23
+ .replace(/[\r\n]+/g, " ")
24
+ .replace(/\s+/g, " ")
25
+ .trim();
26
+ if (sanitized.length <= MAX_TITLE_LENGTH) {
27
+ return sanitized;
28
+ }
29
+ return sanitized.slice(0, MAX_TITLE_LENGTH - 1) + "…";
30
+ }
14
31
  // ==================== 超时配置 ====================
15
32
  // 可通过环境变量覆盖默认值
16
33
  /** 获取模型列表超时时间(毫秒),默认 30 秒 */
@@ -53,6 +70,7 @@ async function withTimeout(promise, timeoutMs, operationName, logger = console)
53
70
  }
54
71
  // Bypass Permissions doesn't work if we are a root/sudo user
55
72
  const IS_ROOT = (process.geteuid?.() ?? process.getuid?.()) === 0;
73
+ const ALLOW_BYPASS = !IS_ROOT || !!process.env.IS_SANDBOX;
56
74
  // Implement the ACP Agent interface
57
75
  export class ClaudeAcpAgent {
58
76
  constructor(client, logger) {
@@ -71,16 +89,16 @@ export class ClaudeAcpAgent {
71
89
  id: "claude-login",
72
90
  };
73
91
  // If client supports terminal-auth capability, use that instead.
74
- // if (request.clientCapabilities?._meta?.["terminal-auth"] === true) {
75
- // const cliPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk/cli.js"));
76
- // authMethod._meta = {
77
- // "terminal-auth": {
78
- // command: "node",
79
- // args: [cliPath, "/login"],
80
- // label: "Claude Code Login",
81
- // },
82
- // };
83
- // }
92
+ if (request.clientCapabilities?._meta?.["terminal-auth"] === true) {
93
+ const cliPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk/cli.js"));
94
+ authMethod._meta = {
95
+ "terminal-auth": {
96
+ command: "node",
97
+ args: [cliPath, "/login"],
98
+ label: "Claude Code Login",
99
+ },
100
+ };
101
+ }
84
102
  return {
85
103
  protocolVersion: 1,
86
104
  agentCapabilities: {
@@ -92,8 +110,10 @@ export class ClaudeAcpAgent {
92
110
  http: true,
93
111
  sse: true,
94
112
  },
113
+ loadSession: true,
95
114
  sessionCapabilities: {
96
115
  fork: {},
116
+ list: {},
97
117
  resume: {},
98
118
  },
99
119
  },
@@ -135,6 +155,171 @@ export class ClaudeAcpAgent {
135
155
  });
136
156
  return response;
137
157
  }
158
+ async loadSession(params) {
159
+ try {
160
+ await fs.promises.access(sessionFilePath(params.cwd, params.sessionId));
161
+ }
162
+ catch {
163
+ throw new Error("Session not found");
164
+ }
165
+ const response = await this.createSession({
166
+ cwd: params.cwd,
167
+ mcpServers: params.mcpServers ?? [],
168
+ _meta: params._meta,
169
+ }, {
170
+ resume: params.sessionId,
171
+ });
172
+ await this.replaySessionHistory(params.sessionId, params.cwd);
173
+ return {
174
+ modes: response.modes,
175
+ models: response.models,
176
+ };
177
+ }
178
+ /**
179
+ * List Claude Code sessions by parsing JSONL files
180
+ * Sessions are stored in ~/.claude/projects/<path-encoded>/
181
+ * Implements the draft session/list RFD spec
182
+ */
183
+ async unstable_listSessions(params) {
184
+ // Note: We load all sessions into memory for sorting, so pagination here is for
185
+ // API response size limits rather than memory efficiency. This matches the RFD spec.
186
+ const PAGE_SIZE = 50;
187
+ const claudeDir = path.join(CLAUDE_CONFIG_DIR, "projects");
188
+ try {
189
+ await fs.promises.access(claudeDir);
190
+ }
191
+ catch {
192
+ return { sessions: [] };
193
+ }
194
+ // Collect all sessions across all project directories
195
+ const allSessions = [];
196
+ const encodedCwdFilter = params.cwd ? encodeProjectPath(params.cwd) : null;
197
+ try {
198
+ const projectDirs = await fs.promises.readdir(claudeDir);
199
+ for (const encodedPath of projectDirs) {
200
+ const projectDir = path.join(claudeDir, encodedPath);
201
+ const stat = await fs.promises.stat(projectDir);
202
+ if (!stat.isDirectory())
203
+ continue;
204
+ // Path encoding is not always reversible (hyphens can be separators or literals),
205
+ // so only use encoded value as a coarse pre-filter.
206
+ if (encodedCwdFilter && encodedPath !== encodedCwdFilter)
207
+ continue;
208
+ const files = await fs.promises.readdir(projectDir);
209
+ // Filter to user session files only. Skip agent-*.jsonl files which contain
210
+ // internal agent metadata and system logs, not user-visible conversation sessions.
211
+ const jsonlFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
212
+ for (const file of jsonlFiles) {
213
+ const filePath = path.join(projectDir, file);
214
+ try {
215
+ const content = await fs.promises.readFile(filePath, "utf-8");
216
+ const lines = content.trim().split("\n").filter(Boolean);
217
+ const sessionId = file.replace(".jsonl", "");
218
+ let parsedAnyEntry = false;
219
+ let sessionCwd;
220
+ // Find first user message for title
221
+ let title;
222
+ for (const line of lines) {
223
+ try {
224
+ const entry = JSON.parse(line);
225
+ parsedAnyEntry = true;
226
+ if (entry.isSidechain === true) {
227
+ continue;
228
+ }
229
+ const entrySessionId = typeof entry.sessionId === "string" ? entry.sessionId : undefined;
230
+ if (typeof entry.sessionId === "string" && entry.sessionId !== entrySessionId) {
231
+ continue;
232
+ }
233
+ if (typeof entry.cwd === "string") {
234
+ sessionCwd = entry.cwd;
235
+ }
236
+ if (!title && entry.type === "user" && entry.message?.content) {
237
+ const msgContent = entry.message.content;
238
+ if (typeof msgContent === "string") {
239
+ title = sanitizeTitle(msgContent);
240
+ }
241
+ if (Array.isArray(msgContent) && msgContent.length > 0) {
242
+ const first = msgContent[0];
243
+ const text = typeof first === "string"
244
+ ? first
245
+ : first && typeof first === "object" && typeof first.text === "string"
246
+ ? first.text
247
+ : undefined;
248
+ if (text) {
249
+ title = sanitizeTitle(text);
250
+ }
251
+ }
252
+ }
253
+ // Continue scanning until we have both fields, since cwd can appear
254
+ // in later entries even after the first user title-bearing message.
255
+ if (title && sessionCwd) {
256
+ break;
257
+ }
258
+ }
259
+ catch {
260
+ // Skip malformed lines
261
+ }
262
+ }
263
+ if (!parsedAnyEntry)
264
+ continue;
265
+ // SessionInfo.cwd is currently required. For entries that do not
266
+ // include an explicit cwd in the session JSONL (typically metadata-only files),
267
+ // we skip them instead of decoding folder names because path encoding is lossy.
268
+ if (!sessionCwd)
269
+ continue;
270
+ // Even after encoded-path pre-filtering, verify per-entry cwd to disambiguate
271
+ // collisions such as "/a-b" and "/a/b" that map to the same encoded folder name.
272
+ if (params.cwd && sessionCwd !== params.cwd)
273
+ continue;
274
+ // Get file modification time as updatedAt
275
+ const fileStat = await fs.promises.stat(filePath);
276
+ const updatedAt = fileStat.mtime.toISOString();
277
+ allSessions.push({
278
+ sessionId,
279
+ cwd: sessionCwd,
280
+ title: title ?? null,
281
+ updatedAt,
282
+ });
283
+ }
284
+ catch (err) {
285
+ this.logger.error(`[unstable_listSessions] Failed to parse session file: ${filePath}`, err);
286
+ }
287
+ }
288
+ }
289
+ }
290
+ catch (err) {
291
+ this.logger.error("[unstable_listSessions] Failed to list sessions", err);
292
+ return { sessions: [] };
293
+ }
294
+ // Sort by updatedAt descending (most recent first)
295
+ allSessions.sort((a, b) => {
296
+ const timeA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
297
+ const timeB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
298
+ return timeB - timeA;
299
+ });
300
+ // Handle pagination with cursor
301
+ let startIndex = 0;
302
+ if (params.cursor) {
303
+ try {
304
+ const decoded = Buffer.from(params.cursor, "base64").toString("utf-8");
305
+ const cursorData = JSON.parse(decoded);
306
+ startIndex = cursorData.offset ?? 0;
307
+ }
308
+ catch {
309
+ // Invalid cursor, start from beginning
310
+ }
311
+ }
312
+ const pageOfSessions = allSessions.slice(startIndex, startIndex + PAGE_SIZE);
313
+ const hasMore = startIndex + PAGE_SIZE < allSessions.length;
314
+ const response = {
315
+ sessions: pageOfSessions,
316
+ };
317
+ if (hasMore) {
318
+ const nextCursor = Buffer.from(JSON.stringify({ offset: startIndex + PAGE_SIZE })).toString("base64");
319
+ response.nextCursor = nextCursor;
320
+ }
321
+ return response;
322
+ }
138
323
  async authenticate(_params) {
139
324
  throw new Error("Method not implemented.");
140
325
  }
@@ -159,8 +344,12 @@ export class ClaudeAcpAgent {
159
344
  case "init":
160
345
  break;
161
346
  case "compact_boundary":
347
+ case "hook_started":
348
+ case "task_notification":
349
+ case "hook_progress":
162
350
  case "hook_response":
163
351
  case "status":
352
+ case "files_persisted":
164
353
  // Todo: process via status api: https://docs.claude.com/en/docs/claude-code/hooks#hook-output
165
354
  break;
166
355
  default:
@@ -215,6 +404,14 @@ export class ClaudeAcpAgent {
215
404
  // their own docs: https://docs.anthropic.com/en/docs/claude-code/sdk/sdk-slash-commands#%2Fcompact-compact-conversation-history
216
405
  if (typeof message.message.content === "string" &&
217
406
  message.message.content.includes("<local-command-stdout>")) {
407
+ // Handle /context by sending its reply as regular agent message.
408
+ if (message.message.content.includes("Context Usage")) {
409
+ for (const notification of toAcpNotifications(message.message.content
410
+ .replace("<local-command-stdout>", "")
411
+ .replace("</local-command-stdout>", ""), "assistant", params.sessionId, this.toolUseCache, this.client, this.logger)) {
412
+ await this.client.sessionUpdate(notification);
413
+ }
414
+ }
218
415
  this.logger.log(message.message.content);
219
416
  break;
220
417
  }
@@ -249,6 +446,7 @@ export class ClaudeAcpAgent {
249
446
  break;
250
447
  }
251
448
  case "tool_progress":
449
+ case "tool_use_summary":
252
450
  break;
253
451
  case "auth_status":
254
452
  break;
@@ -295,6 +493,54 @@ export class ClaudeAcpAgent {
295
493
  throw new Error("Invalid Mode");
296
494
  }
297
495
  }
496
+ async replaySessionHistory(sessionId, cwd) {
497
+ const filePath = sessionFilePath(cwd, sessionId);
498
+ const toolUseCache = {};
499
+ const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
500
+ const reader = readline.createInterface({ input: stream, crlfDelay: Infinity });
501
+ try {
502
+ for await (const line of reader) {
503
+ const trimmed = line.trim();
504
+ if (!trimmed) {
505
+ continue;
506
+ }
507
+ let entry;
508
+ try {
509
+ entry = JSON.parse(trimmed);
510
+ }
511
+ catch {
512
+ continue;
513
+ }
514
+ if (entry.type !== "user" && entry.type !== "assistant") {
515
+ continue;
516
+ }
517
+ if (entry.isSidechain) {
518
+ continue;
519
+ }
520
+ if (entry.sessionId && entry.sessionId !== sessionId) {
521
+ continue;
522
+ }
523
+ const message = entry.message;
524
+ if (!message) {
525
+ continue;
526
+ }
527
+ const role = message.role === "assistant" ? "assistant" : message.role === "user" ? "user" : null;
528
+ if (!role) {
529
+ continue;
530
+ }
531
+ const content = message.content;
532
+ if (typeof content !== "string" && !Array.isArray(content)) {
533
+ continue;
534
+ }
535
+ for (const notification of toAcpNotifications(content, role, sessionId, toolUseCache, this.client, this.logger, { registerHooks: false })) {
536
+ await this.client.sessionUpdate(notification);
537
+ }
538
+ }
539
+ }
540
+ finally {
541
+ reader.close();
542
+ }
543
+ }
298
544
  async readTextFile(params) {
299
545
  const response = await this.client.readTextFile(params);
300
546
  return response;
@@ -487,11 +733,6 @@ export class ClaudeAcpAgent {
487
733
  const permissionMode = "default";
488
734
  // Extract options from _meta if provided
489
735
  const userProvidedOptions = params._meta?.claudeCode?.options;
490
- const extraArgs = { ...userProvidedOptions?.extraArgs };
491
- if (creationOpts?.resume === undefined || creationOpts?.forkSession) {
492
- // Set our own session id if not resuming an existing session.
493
- extraArgs["session-id"] = sessionId;
494
- }
495
736
  // Configure thinking tokens from environment variable
496
737
  const maxThinkingTokens = process.env.MAX_THINKING_TOKENS
497
738
  ? parseInt(process.env.MAX_THINKING_TOKENS, 10)
@@ -506,10 +747,9 @@ export class ClaudeAcpAgent {
506
747
  cwd: params.cwd,
507
748
  includePartialMessages: true,
508
749
  mcpServers: { ...(userProvidedOptions?.mcpServers || {}), ...mcpServers },
509
- extraArgs,
510
750
  // If we want bypassPermissions to be an option, we have to allow it here.
511
751
  // But it doesn't work in root mode, so we only activate it if it will work.
512
- allowDangerouslySkipPermissions: !IS_ROOT,
752
+ allowDangerouslySkipPermissions: ALLOW_BYPASS,
513
753
  permissionMode,
514
754
  canUseTool: this.canUseTool(sessionId),
515
755
  // note: although not documented by the types, passing an absolute path
@@ -530,12 +770,32 @@ export class ClaudeAcpAgent {
530
770
  PostToolUse: [
531
771
  ...(userProvidedOptions?.hooks?.PostToolUse || []),
532
772
  {
533
- hooks: [createPostToolUseHook(this.logger)],
773
+ hooks: [
774
+ createPostToolUseHook(this.logger, {
775
+ onEnterPlanMode: async () => {
776
+ const session = this.sessions[sessionId];
777
+ if (session) {
778
+ session.permissionMode = "plan";
779
+ }
780
+ await this.client.sessionUpdate({
781
+ sessionId,
782
+ update: {
783
+ sessionUpdate: "current_mode_update",
784
+ currentModeId: "plan",
785
+ },
786
+ });
787
+ },
788
+ }),
789
+ ],
534
790
  },
535
791
  ],
536
792
  },
537
793
  ...creationOpts,
538
794
  };
795
+ if (creationOpts?.resume === undefined || creationOpts?.forkSession) {
796
+ // Set our own session id if not resuming an existing session.
797
+ options.sessionId = sessionId;
798
+ }
539
799
  const allowedTools = [];
540
800
  // Disable this for now, not a great way to expose this over ACP at the moment (in progress work so we can revisit)
541
801
  const disallowedTools = ["AskUserQuestion"];
@@ -562,7 +822,7 @@ export class ClaudeAcpAgent {
562
822
  options.allowedTools = allowedTools;
563
823
  }
564
824
  if (disallowedTools.length > 0) {
565
- options.disallowedTools = disallowedTools;
825
+ options.disallowedTools = [...(options.disallowedTools || []), ...disallowedTools];
566
826
  }
567
827
  // Handle abort controller from meta options
568
828
  const abortController = userProvidedOptions?.abortController;
@@ -589,47 +849,10 @@ export class ClaudeAcpAgent {
589
849
  permissionMode,
590
850
  settingsManager,
591
851
  };
592
- this.logger.log(`📋 [ACP] 开始获取可用命令和模型...`);
593
- // 使用超时保护包装命令获取
594
- let availableCommands = [];
595
- // try {
596
- // availableCommands = await withTimeout(
597
- // getAvailableSlashCommands(q, this.logger),
598
- // COMMAND_FETCH_TIMEOUT_MS,
599
- // "getAvailableSlashCommands",
600
- // this.logger,
601
- // );
602
- // } catch (error) {
603
- // if (USE_DEFAULTS_ON_TIMEOUT) {
604
- // this.logger.log(`⚠️ [ACP] 获取命令失败,使用空列表: ${error}`);
605
- // availableCommands = [];
606
- // } else {
607
- // throw error;
608
- // }
609
- // }
610
- // 使用超时保护包装模型获取
611
- let models = {
612
- availableModels: [],
613
- currentModelId: "",
614
- };
615
- // try {
616
- // models = await withTimeout(
617
- // getAvailableModels(q, this.logger),
618
- // MODEL_FETCH_TIMEOUT_MS,
619
- // "getAvailableModels",
620
- // this.logger,
621
- // );
622
- // } catch (error) {
623
- // if (USE_DEFAULTS_ON_TIMEOUT) {
624
- // this.logger.log(`⚠️ [ACP] 获取模型失败,使用默认值: ${error}`);
625
- // models = {
626
- // availableModels: [],
627
- // currentModelId: "",
628
- // };
629
- // } else {
630
- // throw error;
631
- // }
632
- // }
852
+ this.logger.log(`📋 [ACP] 等待初始化结果 (模型+命令)...`);
853
+ const initializationResult = await withTimeout(q.initializationResult(), MODEL_FETCH_TIMEOUT_MS, "initializationResult", this.logger);
854
+ this.logger.log(`📋 [ACP] 初始化完成, 获取到 ${initializationResult.models.length} 个模型, ${initializationResult.commands.length} 个命令`);
855
+ const models = await getAvailableModels(q, initializationResult.models, settingsManager);
633
856
  const sessionCreateElapsed = Date.now() - sessionCreateStartTime;
634
857
  this.logger.log(`✅ [ACP] 会话创建完成, 总耗时: ${sessionCreateElapsed}ms`);
635
858
  // Needs to happen after we return the session
@@ -638,7 +861,7 @@ export class ClaudeAcpAgent {
638
861
  sessionId,
639
862
  update: {
640
863
  sessionUpdate: "available_commands_update",
641
- availableCommands,
864
+ availableCommands: getAvailableSlashCommands(initializationResult.commands),
642
865
  },
643
866
  });
644
867
  }, 0);
@@ -665,7 +888,7 @@ export class ClaudeAcpAgent {
665
888
  },
666
889
  ];
667
890
  // Only works in non-root mode
668
- if (!IS_ROOT) {
891
+ if (ALLOW_BYPASS) {
669
892
  availableModes.push({
670
893
  id: "bypassPermissions",
671
894
  name: "Bypass Permissions",
@@ -682,42 +905,42 @@ export class ClaudeAcpAgent {
682
905
  };
683
906
  }
684
907
  }
685
- async function getAvailableModels(query, logger = console) {
686
- const startTime = Date.now();
687
- logger.log(` 📋 [ACP] 开始获取可用模型列表...`);
688
- const models = await query.supportedModels();
689
- logger.log(` 📋 [ACP] 获取到 ${models.length} 个模型, 耗时: ${Date.now() - startTime}ms`);
690
- // Query doesn't give us access to the currently selected model, so we just choose the first model in the list.
908
+ async function getAvailableModels(query, models, settingsManager) {
691
909
  if (models.length === 0) {
692
- logger.log(` ⚠️ [ACP] 没有可用模型`);
693
910
  return { availableModels: [], currentModelId: "" };
694
911
  }
695
- const currentModel = models[0];
912
+ const settings = settingsManager.getSettings();
913
+ let currentModel = models[0];
914
+ if (settings.model) {
915
+ const match = models.find((m) => m.value === settings.model ||
916
+ m.value.includes(settings.model) ||
917
+ settings.model.includes(m.value) ||
918
+ m.displayName.toLowerCase() === settings.model.toLowerCase() ||
919
+ m.displayName.toLowerCase().includes(settings.model.toLowerCase()));
920
+ if (match) {
921
+ currentModel = match;
922
+ }
923
+ }
696
924
  await query.setModel(currentModel.value);
697
- const availableModels = models.map((model) => ({
698
- modelId: model.value,
699
- name: model.displayName,
700
- description: model.description,
701
- }));
702
925
  return {
703
- availableModels,
926
+ availableModels: models.map((model) => ({
927
+ modelId: model.value,
928
+ name: model.displayName,
929
+ description: model.description,
930
+ })),
704
931
  currentModelId: currentModel.value,
705
932
  };
706
933
  }
707
- async function getAvailableSlashCommands(query, logger = console) {
708
- const startTime = Date.now();
709
- logger.log(` 📋 [ACP] 开始获取可用斜杠命令...`);
934
+ function getAvailableSlashCommands(commands) {
710
935
  const UNSUPPORTED_COMMANDS = [
711
- "context",
712
936
  "cost",
937
+ "keybindings-help",
713
938
  "login",
714
939
  "logout",
715
940
  "output-style:new",
716
941
  "release-notes",
717
942
  "todos",
718
943
  ];
719
- const commands = await query.supportedCommands();
720
- logger.log(` 📋 [ACP] 获取到 ${commands.length} 个斜杠命令, 耗时: ${Date.now() - startTime}ms`);
721
944
  return commands
722
945
  .map((command) => {
723
946
  const input = command.argumentHint
@@ -837,7 +1060,8 @@ export function promptToClaude(prompt) {
837
1060
  * Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
838
1061
  * Only handles text, image, and thinking chunks for now.
839
1062
  */
840
- export function toAcpNotifications(content, role, sessionId, toolUseCache, client, logger) {
1063
+ export function toAcpNotifications(content, role, sessionId, toolUseCache, client, logger, options) {
1064
+ const registerHooks = options?.registerHooks !== false;
841
1065
  if (typeof content === "string") {
842
1066
  return [
843
1067
  {
@@ -902,31 +1126,32 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
902
1126
  }
903
1127
  }
904
1128
  else {
905
- // Register hook callback to receive the structured output from the hook
906
- registerHookCallback(chunk.id, {
907
- onPostToolUseHook: async (toolUseId, toolInput, toolResponse) => {
908
- const toolUse = toolUseCache[toolUseId];
909
- if (toolUse) {
910
- const update = {
911
- _meta: {
912
- claudeCode: {
913
- toolResponse,
914
- toolName: toolUse.name,
1129
+ if (registerHooks) {
1130
+ registerHookCallback(chunk.id, {
1131
+ onPostToolUseHook: async (toolUseId, toolInput, toolResponse) => {
1132
+ const toolUse = toolUseCache[toolUseId];
1133
+ if (toolUse) {
1134
+ const update = {
1135
+ _meta: {
1136
+ claudeCode: {
1137
+ toolResponse,
1138
+ toolName: toolUse.name,
1139
+ },
915
1140
  },
916
- },
917
- toolCallId: toolUseId,
918
- sessionUpdate: "tool_call_update",
919
- };
920
- await client.sessionUpdate({
921
- sessionId,
922
- update,
923
- });
924
- }
925
- else {
926
- logger.error(`[claude-code-acp] Got a tool response for tool use that wasn't tracked: ${toolUseId}`);
927
- }
928
- },
929
- });
1141
+ toolCallId: toolUseId,
1142
+ sessionUpdate: "tool_call_update",
1143
+ };
1144
+ await client.sessionUpdate({
1145
+ sessionId,
1146
+ update,
1147
+ });
1148
+ }
1149
+ else {
1150
+ logger.error(`[claude-code-acp] Got a tool response for tool use that wasn't tracked: ${toolUseId}`);
1151
+ }
1152
+ },
1153
+ });
1154
+ }
930
1155
  let rawInput;
931
1156
  try {
932
1157
  rawInput = JSON.parse(JSON.stringify(chunk.input));
@@ -972,6 +1197,7 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
972
1197
  toolCallId: chunk.tool_use_id,
973
1198
  sessionUpdate: "tool_call_update",
974
1199
  status: "is_error" in chunk && chunk.is_error ? "failed" : "completed",
1200
+ rawOutput: chunk.content,
975
1201
  ...toolUpdateFromToolResult(chunk, toolUseCache[chunk.tool_use_id]),
976
1202
  };
977
1203
  }
@@ -984,6 +1210,8 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
984
1210
  case "citations_delta":
985
1211
  case "signature_delta":
986
1212
  case "container_upload":
1213
+ case "compaction":
1214
+ case "compaction_delta":
987
1215
  break;
988
1216
  default:
989
1217
  unreachable(chunk, logger);
@@ -1 +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,CAgoBX;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"}
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,CA+qBX;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"}
@@ -535,15 +535,54 @@ Output: Create directory 'foo'`),
535
535
 
536
536
  In sessions with ${acpToolNames.bashOutput} always use it for output from Bash commands instead of TaskOutput.`,
537
537
  inputSchema: {
538
- bash_id: z
538
+ task_id: z
539
539
  .string()
540
540
  .describe(`The id of the background bash command as returned by \`${acpToolNames.bash}\``),
541
+ block: z.boolean().describe("Whether to wait for completion"),
542
+ timeout: z.number().describe("Max wait time in ms"),
541
543
  },
542
544
  }, async (input) => {
543
545
  try {
544
- const bgTerm = agent.backgroundTerminals[input.bash_id];
546
+ const bgTerm = agent.backgroundTerminals[input.task_id];
545
547
  if (!bgTerm) {
546
- throw new Error(`Unknown shell ${input.bash_id}`);
548
+ throw new Error(`Unknown shell ${input.task_id}`);
549
+ }
550
+ if (input.block && bgTerm.status === "started") {
551
+ const statusPromise = Promise.race([
552
+ bgTerm.handle
553
+ .waitForExit()
554
+ .then((exitStatus) => ({ status: "exited", exitStatus })),
555
+ sleep(input.timeout ?? 2 * 60 * 1000).then(async () => {
556
+ if (bgTerm.status === "started") {
557
+ await bgTerm.handle.kill();
558
+ }
559
+ return { status: "timedOut", exitStatus: null };
560
+ }),
561
+ ]);
562
+ const { status, exitStatus } = await statusPromise;
563
+ const currentOutput = await bgTerm.handle.currentOutput();
564
+ const strippedOutput = stripCommonPrefix(bgTerm.lastOutput?.output ?? "", currentOutput.output);
565
+ agent.backgroundTerminals[input.task_id] = {
566
+ status,
567
+ pendingOutput: {
568
+ ...currentOutput,
569
+ output: strippedOutput,
570
+ exitStatus: exitStatus ?? currentOutput.exitStatus,
571
+ },
572
+ };
573
+ await bgTerm.handle.release();
574
+ return {
575
+ content: [
576
+ {
577
+ type: "text",
578
+ text: toolCommandOutput(status, {
579
+ ...currentOutput,
580
+ output: strippedOutput,
581
+ exitStatus: exitStatus ?? currentOutput.exitStatus,
582
+ }),
583
+ },
584
+ ],
585
+ };
547
586
  }
548
587
  if (bgTerm.status === "started") {
549
588
  const newOutput = await bgTerm.handle.currentOutput();
@@ -19,6 +19,7 @@ export interface PermissionSettings {
19
19
  export interface ClaudeCodeSettings {
20
20
  permissions?: PermissionSettings;
21
21
  env?: Record<string, string>;
22
+ model?: string;
22
23
  }
23
24
  export type PermissionDecision = "allow" | "deny" | "ask";
24
25
  export interface PermissionCheckResult {
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACnC;AAuLD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;CAC7E;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAC,CAAa;IAC9B,OAAO,CAAC,MAAM,CAAqE;IACnF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAA8C;gBAEvD,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAMzD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAI9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;YACW,eAAe;IAgB7B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IA8CrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkCrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,qBAAqB;IAuC5E;;OAEG;IACH,WAAW,IAAI,kBAAkB;IAIjC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxC;;OAEG;IACH,OAAO,IAAI,IAAI;CAYhB"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACnC;AAwLD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;CAC7E;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAC,CAAa;IAC9B,OAAO,CAAC,MAAM,CAAqE;IACnF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAA8C;gBAEvD,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAMzD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAI9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;YACW,eAAe;IAgB7B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAkDrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkCrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,qBAAqB;IAuC5E;;OAEG;IACH,WAAW,IAAI,kBAAkB;IAIjC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxC;;OAEG;IACH,OAAO,IAAI,IAAI;CAYhB"}
package/dist/settings.js CHANGED
@@ -81,7 +81,8 @@ function normalizePath(filePath, cwd) {
81
81
  else if (!path.isAbsolute(filePath)) {
82
82
  filePath = path.join(cwd, filePath);
83
83
  }
84
- return path.normalize(filePath);
84
+ // Convert backslashes to forward slashes for minimatch compatibility on Windows
85
+ return path.normalize(filePath).replace(/\\/g, "/");
85
86
  }
86
87
  /**
87
88
  * Checks if a file path matches a glob pattern
@@ -103,7 +104,7 @@ function matchesRule(rule, toolName, toolInput, cwd) {
103
104
  // - "Edit rules apply to all built-in tools that edit files."
104
105
  // - "Claude will make a best-effort attempt to apply Read rules to all built-in tools
105
106
  // that read files like Grep, Glob, and LS."
106
- const ruleAppliesToTool = rule.toolName === "Bash" ||
107
+ const ruleAppliesToTool = (rule.toolName === "Bash" && toolName === acpToolNames.bash) ||
107
108
  (rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName)) ||
108
109
  (rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName));
109
110
  if (!ruleAppliesToTool) {
@@ -288,6 +289,9 @@ export class SettingsManager {
288
289
  if (settings.env) {
289
290
  merged.env = { ...merged.env, ...settings.env };
290
291
  }
292
+ if (settings.model) {
293
+ merged.model = settings.model;
294
+ }
291
295
  }
292
296
  this.mergedSettings = merged;
293
297
  }
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { PlanEntry, ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk";
2
2
  import { ToolResultBlockParam, WebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources";
3
+ import { BetaBashCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaRequestMCPToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaWebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources/beta.mjs";
3
4
  export declare const ACP_TOOL_NAME_PREFIX = "mcp__acp__";
4
5
  export declare const acpToolNames: {
5
6
  read: string;
@@ -10,7 +11,6 @@ export declare const acpToolNames: {
10
11
  bashOutput: string;
11
12
  };
12
13
  export declare const EDIT_TOOL_NAMES: string[];
13
- import { BetaBashCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaRequestMCPToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaWebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources/beta.mjs";
14
14
  import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
15
15
  import { Logger } from "./acp-agent.js";
16
16
  import { SettingsManager } from "./settings.js";
@@ -26,7 +26,7 @@ interface ToolUpdate {
26
26
  locations?: ToolCallLocation[];
27
27
  }
28
28
  export declare function toolInfoFromToolUse(toolUse: any): ToolInfo;
29
- export declare function toolUpdateFromToolResult(toolResult: ToolResultBlockParam | BetaWebSearchToolResultBlockParam | BetaWebFetchToolResultBlockParam | WebSearchToolResultBlockParam | BetaCodeExecutionToolResultBlockParam | BetaBashCodeExecutionToolResultBlockParam | BetaTextEditorCodeExecutionToolResultBlockParam | BetaRequestMCPToolResultBlockParam | BetaToolSearchToolResultBlockParam, toolUse: any | undefined): ToolUpdate;
29
+ export declare function toolUpdateFromToolResult(toolResult: ToolResultBlockParam | BetaToolResultBlockParam | BetaWebSearchToolResultBlockParam | BetaWebFetchToolResultBlockParam | WebSearchToolResultBlockParam | BetaCodeExecutionToolResultBlockParam | BetaBashCodeExecutionToolResultBlockParam | BetaTextEditorCodeExecutionToolResultBlockParam | BetaRequestMCPToolResultBlockParam | BetaToolSearchToolResultBlockParam, toolUse: any | undefined): ToolUpdate;
30
30
  export type ClaudePlanEntry = {
31
31
  content: string;
32
32
  status: "pending" | "in_progress" | "completed";
@@ -39,7 +39,9 @@ export declare function markdownEscape(text: string): string;
39
39
  export declare const registerHookCallback: (toolUseID: string, { onPostToolUseHook, }: {
40
40
  onPostToolUseHook?: (toolUseID: string, toolInput: unknown, toolResponse: unknown) => Promise<void>;
41
41
  }) => void;
42
- export declare const createPostToolUseHook: (logger?: Logger) => HookCallback;
42
+ export declare const createPostToolUseHook: (logger?: Logger, options?: {
43
+ onEnterPlanMode?: () => Promise<void>;
44
+ }) => HookCallback;
43
45
  /**
44
46
  * Creates a PreToolUse hook that checks permissions using the SettingsManager.
45
47
  * This runs before the SDK's built-in permission rules, allowing us to enforce
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGlG,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAWlG,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,eAAO,MAAM,YAAY;;;;;;;CAOxB,CAAC;AAEF,eAAO,MAAM,eAAe,UAA0C,CAAC;AAEvE,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,kCAAkC,EAClC,+CAA+C,EAC/C,kCAAkC,EAClC,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,CA+V1D;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EACN,oBAAoB,GACpB,iCAAiC,GACjC,gCAAgC,GAChC,6BAA6B,GAC7B,qCAAqC,GACrC,yCAAyC,GACzC,+CAA+C,GAC/C,kCAAkC,GAClC,kCAAkC,EACtC,OAAO,EAAE,GAAG,GAAG,SAAS,GACvB,UAAU,CA4HZ;AAmCD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,GAAG,SAAS,EAAE,CAM5E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAcD,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,wBAEG;IACD,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,SAKF,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAC/B,SAAQ,MAAgB,KAAG,YAa3B,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC9B,iBAAiB,eAAe,EAAE,SAAQ,MAAgB,KAAG,YA2C7D,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACT,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAGL,oBAAoB,EAEpB,6BAA6B,EAE9B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,yCAAyC,EAGzC,qCAAqC,EAGrC,kCAAkC,EAClC,+CAA+C,EAK/C,wBAAwB,EACxB,kCAAkC,EAIlC,gCAAgC,EAGhC,iCAAiC,EAElC,MAAM,sCAAsC,CAAC;AAW9C,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,eAAO,MAAM,YAAY;;;;;;;CAOxB,CAAC;AAEF,eAAO,MAAM,eAAe,UAA0C,CAAC;AAyBvE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,CA+V1D;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EACN,oBAAoB,GACpB,wBAAwB,GACxB,iCAAiC,GACjC,gCAAgC,GAChC,6BAA6B,GAC7B,qCAAqC,GACrC,yCAAyC,GACzC,+CAA+C,GAC/C,kCAAkC,GAClC,kCAAkC,EACtC,OAAO,EAAE,GAAG,GAAG,SAAS,GACvB,UAAU,CA4HZ;AA0GD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,GAAG,SAAS,EAAE,CAM5E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAcD,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,wBAEG;IACD,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,SAKF,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAE9B,SAAQ,MAAgB,EACxB,UAAU;IACR,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,KACA,YAoBF,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC9B,iBAAiB,eAAe,EAAE,SAAQ,MAAgB,KAAG,YA2C7D,CAAC"}
package/dist/tools.js CHANGED
@@ -448,17 +448,22 @@ export function toolUpdateFromToolResult(toolResult, toolUse) {
448
448
  function toAcpContentUpdate(content, isError = false) {
449
449
  if (Array.isArray(content) && content.length > 0) {
450
450
  return {
451
- content: content.map((content) => ({
451
+ content: content.map((c) => ({
452
452
  type: "content",
453
- content: isError && content.type === "text"
454
- ? {
455
- ...content,
456
- text: `\`\`\`\n${content.text}\n\`\`\``,
457
- }
458
- : content,
453
+ content: toAcpContentBlock(c, isError),
459
454
  })),
460
455
  };
461
456
  }
457
+ else if (typeof content === "object" && content !== null && "type" in content) {
458
+ return {
459
+ content: [
460
+ {
461
+ type: "content",
462
+ content: toAcpContentBlock(content, isError),
463
+ },
464
+ ],
465
+ };
466
+ }
462
467
  else if (typeof content === "string" && content.length > 0) {
463
468
  return {
464
469
  content: [
@@ -474,6 +479,62 @@ function toAcpContentUpdate(content, isError = false) {
474
479
  }
475
480
  return {};
476
481
  }
482
+ function toAcpContentBlock(content, isError) {
483
+ const wrapText = (text) => ({
484
+ type: "text",
485
+ text: isError ? `\`\`\`\n${text}\n\`\`\`` : text,
486
+ });
487
+ switch (content.type) {
488
+ case "text":
489
+ return {
490
+ type: "text",
491
+ text: isError ? `\`\`\`\n${content.text}\n\`\`\`` : content.text,
492
+ };
493
+ case "image":
494
+ if (content.source.type === "base64") {
495
+ return {
496
+ type: "image",
497
+ data: content.source.data,
498
+ mimeType: content.source.media_type,
499
+ };
500
+ }
501
+ // URL and file-based images can't be converted to ACP format (requires data)
502
+ return wrapText(content.source.type === "url"
503
+ ? `[image: ${content.source.url}]`
504
+ : "[image: file reference]");
505
+ case "tool_reference":
506
+ return wrapText(`Tool: ${content.tool_name}`);
507
+ case "tool_search_tool_search_result":
508
+ return wrapText(`Tools found: ${content.tool_references.map((r) => r.tool_name).join(", ") || "none"}`);
509
+ case "tool_search_tool_result_error":
510
+ return wrapText(`Error: ${content.error_code}${content.error_message ? ` - ${content.error_message}` : ""}`);
511
+ case "web_search_result":
512
+ return wrapText(`${content.title} (${content.url})`);
513
+ case "web_search_tool_result_error":
514
+ return wrapText(`Error: ${content.error_code}`);
515
+ case "web_fetch_result":
516
+ return wrapText(`Fetched: ${content.url}`);
517
+ case "web_fetch_tool_result_error":
518
+ return wrapText(`Error: ${content.error_code}`);
519
+ case "code_execution_result":
520
+ return wrapText(`Output: ${content.stdout || content.stderr || ""}`);
521
+ case "bash_code_execution_result":
522
+ return wrapText(`Output: ${content.stdout || content.stderr || ""}`);
523
+ case "code_execution_tool_result_error":
524
+ case "bash_code_execution_tool_result_error":
525
+ return wrapText(`Error: ${content.error_code}`);
526
+ case "text_editor_code_execution_view_result":
527
+ return wrapText(content.content);
528
+ case "text_editor_code_execution_create_result":
529
+ return wrapText(content.is_file_update ? "File updated" : "File created");
530
+ case "text_editor_code_execution_str_replace_result":
531
+ return wrapText(content.lines?.join("\n") || "");
532
+ case "text_editor_code_execution_tool_result_error":
533
+ return wrapText(`Error: ${content.error_code}${content.error_message ? ` - ${content.error_message}` : ""}`);
534
+ default:
535
+ return wrapText(JSON.stringify(content));
536
+ }
537
+ }
477
538
  export function planEntries(input) {
478
539
  return input.todos.map((input) => ({
479
540
  content: input.content,
@@ -499,16 +560,22 @@ export const registerHookCallback = (toolUseID, { onPostToolUseHook, }) => {
499
560
  };
500
561
  };
501
562
  /* A callback for Claude Code that is called when receiving a PostToolUse hook */
502
- export const createPostToolUseHook = (logger = console) => async (input, toolUseID) => {
503
- if (input.hook_event_name === "PostToolUse" && toolUseID) {
504
- const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
505
- if (onPostToolUseHook) {
506
- await onPostToolUseHook(toolUseID, input.tool_input, input.tool_response);
507
- delete toolUseCallbacks[toolUseID]; // Cleanup after execution
563
+ export const createPostToolUseHook = (logger = console, options) => async (input, toolUseID) => {
564
+ if (input.hook_event_name === "PostToolUse") {
565
+ // Handle EnterPlanMode tool - notify client of mode change after successful execution
566
+ if (input.tool_name === "EnterPlanMode" && options?.onEnterPlanMode) {
567
+ await options.onEnterPlanMode();
508
568
  }
509
- else {
510
- logger.error(`No onPostToolUseHook found for tool use ID: ${toolUseID}`);
511
- delete toolUseCallbacks[toolUseID];
569
+ if (toolUseID) {
570
+ const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
571
+ if (onPostToolUseHook) {
572
+ await onPostToolUseHook(toolUseID, input.tool_input, input.tool_response);
573
+ delete toolUseCallbacks[toolUseID]; // Cleanup after execution
574
+ }
575
+ else {
576
+ logger.error(`No onPostToolUseHook found for tool use ID: ${toolUseID}`);
577
+ delete toolUseCallbacks[toolUseID];
578
+ }
512
579
  }
513
580
  }
514
581
  return { continue: true };
package/dist/utils.d.ts CHANGED
@@ -29,4 +29,5 @@ export interface ExtractLinesResult {
29
29
  * @returns Object containing extracted content and metadata
30
30
  */
31
31
  export declare function extractLinesWithByteLimit(fullContent: string, maxContentLength: number): ExtractLinesResult;
32
+ export declare function encodeProjectPath(cwd: string): string;
32
33
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAA0B,MAAM,eAAe,CAAC;AAG3E,qBAAa,QAAQ,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,IAAI,CAAS;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;IASZ,GAAG;IAQH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAgB3C;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAclF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAUlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAgB,QAQjE;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,GAAG,IAAI,CAM/D;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAM3E;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,CA8CpB"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAA0B,MAAM,eAAe,CAAC;AAG3E,qBAAa,QAAQ,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,IAAI,CAAS;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;IASZ,GAAG;IAQH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAgB3C;AAGD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAclF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAUlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAgB,QAQjE;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,GAAG,IAAI,CAM/D;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAM3E;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,CA8CpB;AAKD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUrD"}
package/dist/utils.js CHANGED
@@ -148,3 +148,16 @@ export function extractLinesWithByteLimit(fullContent, maxContentLength) {
148
148
  linesRead: linesSeen,
149
149
  };
150
150
  }
151
+ // Helper to encode a path like Claude does:
152
+ // - Unix: "/Users/test" -> "-Users-test"
153
+ // - Windows: "C:\Users\test" -> "C-Users-test"
154
+ export function encodeProjectPath(cwd) {
155
+ const windowsPathMatch = cwd.match(/^([A-Za-z]):[\\/]/);
156
+ if (windowsPathMatch) {
157
+ const driveLetter = windowsPathMatch[1];
158
+ const rest = cwd.slice(2);
159
+ return `${driveLetter}${rest.replace(/[\\/]/g, "-")}`;
160
+ }
161
+ // Unix paths
162
+ return cwd.replace(/\//g, "-");
163
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.13.1",
6
+ "version": "0.16.0",
7
7
  "description": "An ACP-compatible coding agent powered by the Claude Code SDK (TypeScript)",
8
8
  "main": "dist/lib.js",
9
9
  "types": "dist/lib.d.ts",
@@ -60,23 +60,23 @@
60
60
  "author": "Zed Industries",
61
61
  "license": "Apache-2.0",
62
62
  "dependencies": {
63
- "@agentclientprotocol/sdk": "0.13.0",
64
- "@anthropic-ai/claude-agent-sdk": "0.2.7",
65
- "@modelcontextprotocol/sdk": "1.25.2",
63
+ "@agentclientprotocol/sdk": "0.14.1",
64
+ "@anthropic-ai/claude-agent-sdk": "0.2.34",
65
+ "@modelcontextprotocol/sdk": "1.26.0",
66
66
  "diff": "8.0.3",
67
- "minimatch": "10.1.1"
67
+ "minimatch": "10.1.2"
68
68
  },
69
69
  "devDependencies": {
70
- "@anthropic-ai/sdk": "0.71.2",
71
- "@types/node": "25.0.8",
72
- "@typescript-eslint/eslint-plugin": "8.53.0",
73
- "@typescript-eslint/parser": "8.53.0",
70
+ "@anthropic-ai/sdk": "0.73.0",
71
+ "@types/node": "25.2.1",
72
+ "@typescript-eslint/eslint-plugin": "8.54.0",
73
+ "@typescript-eslint/parser": "8.54.0",
74
74
  "eslint": "9.39.2",
75
75
  "eslint-config-prettier": "10.1.8",
76
- "globals": "17.0.0",
77
- "prettier": "3.8.0",
76
+ "globals": "17.3.0",
77
+ "prettier": "3.8.1",
78
78
  "ts-node": "10.9.2",
79
79
  "typescript": "5.9.3",
80
- "vitest": "4.0.17"
80
+ "vitest": "4.0.18"
81
81
  }
82
82
  }