@ape-egg/claude-code-acp 0.0.0-stage → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Permission rule format examples:
3
+ * - "Read" - matches all Read tool calls
4
+ * - "Read(./.env)" - matches specific path
5
+ * - "Read(./.env.*)" - glob pattern
6
+ * - "Read(./secrets/**)" - recursive glob
7
+ * - "Bash(npm run lint)" - exact command prefix
8
+ * - "Bash(npm run:*)" - command prefix with wildcard
9
+ *
10
+ * Docs: https://code.claude.com/docs/en/iam#tool-specific-permission-rules
11
+ */
12
+ export interface PermissionSettings {
13
+ allow?: string[];
14
+ deny?: string[];
15
+ ask?: string[];
16
+ additionalDirectories?: string[];
17
+ defaultMode?: string;
18
+ }
19
+ export interface ClaudeCodeSettings {
20
+ permissions?: PermissionSettings;
21
+ env?: Record<string, string>;
22
+ model?: string;
23
+ }
24
+ export type PermissionDecision = "allow" | "deny" | "ask";
25
+ export interface PermissionCheckResult {
26
+ decision: PermissionDecision;
27
+ rule?: string;
28
+ source?: "allow" | "deny" | "ask";
29
+ }
30
+ /**
31
+ * Gets the enterprise settings path based on the current platform
32
+ */
33
+ export declare function getManagedSettingsPath(): string;
34
+ export interface SettingsManagerOptions {
35
+ onChange?: () => void;
36
+ logger?: {
37
+ log: (...args: any[]) => void;
38
+ error: (...args: any[]) => void;
39
+ };
40
+ }
41
+ /**
42
+ * Manages Claude Code settings from multiple sources with proper precedence.
43
+ *
44
+ * Settings are loaded from (in order of increasing precedence):
45
+ * 1. User settings (~/.claude/settings.json)
46
+ * 2. Project settings (<cwd>/.claude/settings.json)
47
+ * 3. Local project settings (<cwd>/.claude/settings.local.json)
48
+ * 4. Enterprise managed settings (platform-specific path)
49
+ *
50
+ * The manager watches all settings files for changes and automatically reloads.
51
+ */
52
+ export declare class SettingsManager {
53
+ private cwd;
54
+ private userSettings;
55
+ private projectSettings;
56
+ private localSettings;
57
+ private enterpriseSettings;
58
+ private mergedSettings;
59
+ private watchers;
60
+ private onChange?;
61
+ private logger;
62
+ private initialized;
63
+ private debounceTimer;
64
+ constructor(cwd: string, options?: SettingsManagerOptions);
65
+ /**
66
+ * Initialize the settings manager by loading all settings and setting up file watchers
67
+ */
68
+ initialize(): Promise<void>;
69
+ /**
70
+ * Returns the path to the user settings file
71
+ */
72
+ private getUserSettingsPath;
73
+ /**
74
+ * Returns the path to the project settings file
75
+ */
76
+ private getProjectSettingsPath;
77
+ /**
78
+ * Returns the path to the local project settings file
79
+ */
80
+ private getLocalSettingsPath;
81
+ /**
82
+ * Loads settings from all sources
83
+ */
84
+ private loadAllSettings;
85
+ /**
86
+ * Merges all settings sources with proper precedence.
87
+ * For permissions, rules from all sources are combined.
88
+ * Deny rules always take precedence during permission checks.
89
+ */
90
+ private mergeSettings;
91
+ /**
92
+ * Sets up file watchers for all settings files
93
+ */
94
+ private setupWatchers;
95
+ /**
96
+ * Handles settings file changes with debouncing to avoid rapid reloads
97
+ */
98
+ private handleSettingsChange;
99
+ /**
100
+ * Checks if a tool invocation is allowed based on the loaded settings.
101
+ *
102
+ * @param toolName - The tool name (can be ACP-prefixed like mcp__acp__Read or plain like Read)
103
+ * @param toolInput - The tool input object
104
+ * @returns The permission decision and matching rule info
105
+ */
106
+ checkPermission(toolName: string, toolInput: unknown): PermissionCheckResult;
107
+ /**
108
+ * Returns the current merged settings
109
+ */
110
+ getSettings(): ClaudeCodeSettings;
111
+ /**
112
+ * Returns the current working directory
113
+ */
114
+ getCwd(): string;
115
+ /**
116
+ * Updates the working directory and reloads project-specific settings
117
+ */
118
+ setCwd(cwd: string): Promise<void>;
119
+ /**
120
+ * Disposes of file watchers and cleans up resources
121
+ */
122
+ dispose(): void;
123
+ }
124
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +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;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"}
@@ -0,0 +1,426 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import * as os from "node:os";
4
+ import { minimatch } from "minimatch";
5
+ import { ACP_TOOL_NAME_PREFIX, acpToolNames } from "./tools.js";
6
+ import { CLAUDE_CONFIG_DIR } from "./acp-agent.js";
7
+ /**
8
+ * Shell operators that can be used for command chaining/injection
9
+ * These should cause a prefix match to fail to prevent bypasses like:
10
+ * - "safe-cmd && malicious-cmd"
11
+ * - "safe-cmd; malicious-cmd"
12
+ * - "safe-cmd | malicious-cmd"
13
+ * - "safe-cmd || malicious-cmd"
14
+ * - "$(malicious-cmd)"
15
+ * - "`malicious-cmd`"
16
+ */
17
+ const SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
18
+ /**
19
+ * Checks if a string contains shell operators that could allow command chaining
20
+ */
21
+ function containsShellOperator(str) {
22
+ return SHELL_OPERATORS.some((op) => str.includes(op));
23
+ }
24
+ /*
25
+ * Tools that modify files. Per Claude Code docs:
26
+ * "Edit rules apply to all built-in tools that edit files."
27
+ * This means an Edit(...) rule should match Write, MultiEdit, etc.
28
+ */
29
+ const FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
30
+ /**
31
+ * Tools that read files. Per Claude Code docs:
32
+ * "Claude will make a best-effort attempt to apply Read rules to all built-in tools
33
+ * that read files like Grep and Glob."
34
+ * This means a Read(...) rule should match Grep, Glob, etc.
35
+ */
36
+ const FILE_READING_TOOLS = [acpToolNames.read];
37
+ /**
38
+ * Functions to extract the relevant argument from tool input for permission matching
39
+ */
40
+ const TOOL_ARG_ACCESSORS = {
41
+ mcp__acp__Read: (input) => input?.file_path,
42
+ mcp__acp__Edit: (input) => input?.file_path,
43
+ mcp__acp__Write: (input) => input?.file_path,
44
+ mcp__acp__Bash: (input) => input?.command,
45
+ };
46
+ /**
47
+ * Parses a permission rule string into its components
48
+ * Examples:
49
+ * "Read" -> { toolName: "Read" }
50
+ * "Read(./.env)" -> { toolName: "Read", argument: "./.env" }
51
+ * "Bash(npm run:*)" -> { toolName: "Bash", argument: "npm run", isWildcard: true }
52
+ */
53
+ function parseRule(rule) {
54
+ const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
55
+ if (!match) {
56
+ return { toolName: rule };
57
+ }
58
+ const [, toolName, argument] = match;
59
+ if (argument && argument.endsWith(":*")) {
60
+ return {
61
+ toolName,
62
+ argument: argument.slice(0, -2),
63
+ isWildcard: true,
64
+ };
65
+ }
66
+ return { toolName, argument };
67
+ }
68
+ /**
69
+ * Normalizes a path for comparison:
70
+ * - Expands ~ to home directory
71
+ * - Resolves relative paths against cwd
72
+ * - Normalizes path separators
73
+ */
74
+ function normalizePath(filePath, cwd) {
75
+ if (filePath.startsWith("~/")) {
76
+ filePath = path.join(os.homedir(), filePath.slice(2));
77
+ }
78
+ else if (filePath.startsWith("./")) {
79
+ filePath = path.join(cwd, filePath.slice(2));
80
+ }
81
+ else if (!path.isAbsolute(filePath)) {
82
+ filePath = path.join(cwd, filePath);
83
+ }
84
+ // Convert backslashes to forward slashes for minimatch compatibility on Windows
85
+ return path.normalize(filePath).replace(/\\/g, "/");
86
+ }
87
+ /**
88
+ * Checks if a file path matches a glob pattern
89
+ */
90
+ function matchesGlob(pattern, filePath, cwd) {
91
+ const normalizedPattern = normalizePath(pattern, cwd);
92
+ const normalizedPath = normalizePath(filePath, cwd);
93
+ return minimatch(normalizedPath, normalizedPattern, {
94
+ dot: true,
95
+ matchBase: false,
96
+ nocase: process.platform === "win32",
97
+ });
98
+ }
99
+ /**
100
+ * Checks if a tool invocation matches a parsed permission rule
101
+ */
102
+ function matchesRule(rule, toolName, toolInput, cwd) {
103
+ // Per Claude Code docs:
104
+ // - "Edit rules apply to all built-in tools that edit files."
105
+ // - "Claude will make a best-effort attempt to apply Read rules to all built-in tools
106
+ // that read files like Grep, Glob, and LS."
107
+ const ruleAppliesToTool = (rule.toolName === "Bash" && toolName === acpToolNames.bash) ||
108
+ (rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName)) ||
109
+ (rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName));
110
+ if (!ruleAppliesToTool) {
111
+ return false;
112
+ }
113
+ if (!rule.argument) {
114
+ return true;
115
+ }
116
+ const argAccessor = TOOL_ARG_ACCESSORS[toolName];
117
+ if (!argAccessor) {
118
+ return true;
119
+ }
120
+ const actualArg = argAccessor(toolInput);
121
+ if (!actualArg) {
122
+ return false;
123
+ }
124
+ if (toolName === acpToolNames.bash) {
125
+ // Per Claude Code docs: https://code.claude.com/docs/en/iam#tool-specific-permission-rules
126
+ // - Bash(npm run build) matches the EXACT command "npm run build"
127
+ // - Bash(npm run test:*) matches commands STARTING WITH "npm run test"
128
+ // The :* suffix enables prefix matching, without it the match is exact
129
+ //
130
+ // Also from docs: "Claude Code is aware of shell operators (like &&) so a prefix match
131
+ // rule like Bash(safe-cmd:*) won't give it permission to run the command safe-cmd && other-cmd"
132
+ if (rule.isWildcard) {
133
+ if (!actualArg.startsWith(rule.argument)) {
134
+ return false;
135
+ }
136
+ // Check that the matched prefix isn't followed by shell operators that could
137
+ // allow command chaining/injection
138
+ const remainder = actualArg.slice(rule.argument.length);
139
+ if (containsShellOperator(remainder)) {
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+ return actualArg === rule.argument;
145
+ }
146
+ // For file-based tools (Read, Edit, Write), use glob matching
147
+ return matchesGlob(rule.argument, actualArg, cwd);
148
+ }
149
+ /**
150
+ * Reads and parses a JSON settings file, returning an empty object if not found or invalid
151
+ */
152
+ async function loadSettingsFile(filePath) {
153
+ if (!filePath) {
154
+ return {};
155
+ }
156
+ try {
157
+ const content = await fs.promises.readFile(filePath, "utf-8");
158
+ return JSON.parse(content);
159
+ }
160
+ catch {
161
+ return {};
162
+ }
163
+ }
164
+ /**
165
+ * Gets the enterprise settings path based on the current platform
166
+ */
167
+ export function getManagedSettingsPath() {
168
+ switch (process.platform) {
169
+ case "darwin":
170
+ return "/Library/Application Support/ClaudeCode/managed-settings.json";
171
+ case "linux":
172
+ return "/etc/claude-code/managed-settings.json";
173
+ case "win32":
174
+ return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
175
+ default:
176
+ return "/etc/claude-code/managed-settings.json";
177
+ }
178
+ }
179
+ /**
180
+ * Manages Claude Code settings from multiple sources with proper precedence.
181
+ *
182
+ * Settings are loaded from (in order of increasing precedence):
183
+ * 1. User settings (~/.claude/settings.json)
184
+ * 2. Project settings (<cwd>/.claude/settings.json)
185
+ * 3. Local project settings (<cwd>/.claude/settings.local.json)
186
+ * 4. Enterprise managed settings (platform-specific path)
187
+ *
188
+ * The manager watches all settings files for changes and automatically reloads.
189
+ */
190
+ export class SettingsManager {
191
+ constructor(cwd, options) {
192
+ this.userSettings = {};
193
+ this.projectSettings = {};
194
+ this.localSettings = {};
195
+ this.enterpriseSettings = {};
196
+ this.mergedSettings = {};
197
+ this.watchers = [];
198
+ this.initialized = false;
199
+ this.debounceTimer = null;
200
+ this.cwd = cwd;
201
+ this.onChange = options?.onChange;
202
+ this.logger = options?.logger ?? console;
203
+ }
204
+ /**
205
+ * Initialize the settings manager by loading all settings and setting up file watchers
206
+ */
207
+ async initialize() {
208
+ if (this.initialized) {
209
+ return;
210
+ }
211
+ await this.loadAllSettings();
212
+ this.setupWatchers();
213
+ this.initialized = true;
214
+ }
215
+ /**
216
+ * Returns the path to the user settings file
217
+ */
218
+ getUserSettingsPath() {
219
+ return path.join(CLAUDE_CONFIG_DIR, "settings.json");
220
+ }
221
+ /**
222
+ * Returns the path to the project settings file
223
+ */
224
+ getProjectSettingsPath() {
225
+ return path.join(this.cwd, ".claude", "settings.json");
226
+ }
227
+ /**
228
+ * Returns the path to the local project settings file
229
+ */
230
+ getLocalSettingsPath() {
231
+ return path.join(this.cwd, ".claude", "settings.local.json");
232
+ }
233
+ /**
234
+ * Loads settings from all sources
235
+ */
236
+ async loadAllSettings() {
237
+ const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
238
+ loadSettingsFile(this.getUserSettingsPath()),
239
+ loadSettingsFile(this.getProjectSettingsPath()),
240
+ loadSettingsFile(this.getLocalSettingsPath()),
241
+ loadSettingsFile(getManagedSettingsPath()),
242
+ ]);
243
+ this.userSettings = userSettings;
244
+ this.projectSettings = projectSettings;
245
+ this.localSettings = localSettings;
246
+ this.enterpriseSettings = enterpriseSettings;
247
+ this.mergeSettings();
248
+ }
249
+ /**
250
+ * Merges all settings sources with proper precedence.
251
+ * For permissions, rules from all sources are combined.
252
+ * Deny rules always take precedence during permission checks.
253
+ */
254
+ mergeSettings() {
255
+ const allSettings = [
256
+ this.userSettings,
257
+ this.projectSettings,
258
+ this.localSettings,
259
+ this.enterpriseSettings,
260
+ ];
261
+ const merged = {
262
+ permissions: {
263
+ allow: [],
264
+ deny: [],
265
+ ask: [],
266
+ },
267
+ };
268
+ for (const settings of allSettings) {
269
+ if (settings.permissions) {
270
+ if (settings.permissions.allow) {
271
+ merged.permissions.allow.push(...settings.permissions.allow);
272
+ }
273
+ if (settings.permissions.deny) {
274
+ merged.permissions.deny.push(...settings.permissions.deny);
275
+ }
276
+ if (settings.permissions.ask) {
277
+ merged.permissions.ask.push(...settings.permissions.ask);
278
+ }
279
+ if (settings.permissions.additionalDirectories) {
280
+ merged.permissions.additionalDirectories = [
281
+ ...(merged.permissions.additionalDirectories || []),
282
+ ...settings.permissions.additionalDirectories,
283
+ ];
284
+ }
285
+ if (settings.permissions.defaultMode) {
286
+ merged.permissions.defaultMode = settings.permissions.defaultMode;
287
+ }
288
+ }
289
+ if (settings.env) {
290
+ merged.env = { ...merged.env, ...settings.env };
291
+ }
292
+ if (settings.model) {
293
+ merged.model = settings.model;
294
+ }
295
+ }
296
+ this.mergedSettings = merged;
297
+ }
298
+ /**
299
+ * Sets up file watchers for all settings files
300
+ */
301
+ setupWatchers() {
302
+ const paths = [
303
+ this.getUserSettingsPath(),
304
+ this.getProjectSettingsPath(),
305
+ this.getLocalSettingsPath(),
306
+ getManagedSettingsPath(),
307
+ ];
308
+ for (const filePath of paths) {
309
+ if (!filePath)
310
+ continue;
311
+ try {
312
+ const dir = path.dirname(filePath);
313
+ const filename = path.basename(filePath);
314
+ if (fs.existsSync(dir)) {
315
+ const watcher = fs.watch(dir, (eventType, changedFilename) => {
316
+ if (changedFilename === filename) {
317
+ this.handleSettingsChange();
318
+ }
319
+ });
320
+ watcher.on("error", (error) => {
321
+ this.logger.error(`Settings watcher error for ${filePath}:`, error);
322
+ });
323
+ this.watchers.push(watcher);
324
+ }
325
+ }
326
+ catch (error) {
327
+ this.logger.error(`Failed to set up watcher for ${filePath}:`, error);
328
+ }
329
+ }
330
+ }
331
+ /**
332
+ * Handles settings file changes with debouncing to avoid rapid reloads
333
+ */
334
+ handleSettingsChange() {
335
+ if (this.debounceTimer) {
336
+ clearTimeout(this.debounceTimer);
337
+ }
338
+ this.debounceTimer = setTimeout(async () => {
339
+ this.debounceTimer = null;
340
+ try {
341
+ await this.loadAllSettings();
342
+ this.onChange?.();
343
+ }
344
+ catch (error) {
345
+ this.logger.error("Failed to reload settings:", error);
346
+ }
347
+ }, 100);
348
+ }
349
+ /**
350
+ * Checks if a tool invocation is allowed based on the loaded settings.
351
+ *
352
+ * @param toolName - The tool name (can be ACP-prefixed like mcp__acp__Read or plain like Read)
353
+ * @param toolInput - The tool input object
354
+ * @returns The permission decision and matching rule info
355
+ */
356
+ checkPermission(toolName, toolInput) {
357
+ if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
358
+ return { decision: "ask" };
359
+ }
360
+ const permissions = this.mergedSettings.permissions;
361
+ if (!permissions) {
362
+ return { decision: "ask" };
363
+ }
364
+ // Check deny rules first (highest priority)
365
+ for (const rule of permissions.deny || []) {
366
+ const parsed = parseRule(rule);
367
+ if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
368
+ return { decision: "deny", rule, source: "deny" };
369
+ }
370
+ }
371
+ // Check allow rules
372
+ for (const rule of permissions.allow || []) {
373
+ const parsed = parseRule(rule);
374
+ if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
375
+ return { decision: "allow", rule, source: "allow" };
376
+ }
377
+ }
378
+ // Check ask rules
379
+ for (const rule of permissions.ask || []) {
380
+ const parsed = parseRule(rule);
381
+ if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
382
+ return { decision: "ask", rule, source: "ask" };
383
+ }
384
+ }
385
+ // No matching rule - default to ask
386
+ return { decision: "ask" };
387
+ }
388
+ /**
389
+ * Returns the current merged settings
390
+ */
391
+ getSettings() {
392
+ return this.mergedSettings;
393
+ }
394
+ /**
395
+ * Returns the current working directory
396
+ */
397
+ getCwd() {
398
+ return this.cwd;
399
+ }
400
+ /**
401
+ * Updates the working directory and reloads project-specific settings
402
+ */
403
+ async setCwd(cwd) {
404
+ if (this.cwd === cwd) {
405
+ return;
406
+ }
407
+ this.dispose();
408
+ this.cwd = cwd;
409
+ this.initialized = false;
410
+ await this.initialize();
411
+ }
412
+ /**
413
+ * Disposes of file watchers and cleans up resources
414
+ */
415
+ dispose() {
416
+ if (this.debounceTimer) {
417
+ clearTimeout(this.debounceTimer);
418
+ this.debounceTimer = null;
419
+ }
420
+ for (const watcher of this.watchers) {
421
+ watcher.close();
422
+ }
423
+ this.watchers = [];
424
+ this.initialized = false;
425
+ }
426
+ }
@@ -0,0 +1,52 @@
1
+ import { PlanEntry, ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk";
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";
4
+ export declare const ACP_TOOL_NAME_PREFIX = "mcp__acp__";
5
+ export declare const acpToolNames: {
6
+ read: string;
7
+ edit: string;
8
+ write: string;
9
+ bash: string;
10
+ killShell: string;
11
+ bashOutput: string;
12
+ };
13
+ export declare const EDIT_TOOL_NAMES: string[];
14
+ import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
15
+ import { Logger } from "./acp-agent.js";
16
+ import { SettingsManager } from "./settings.js";
17
+ interface ToolInfo {
18
+ title: string;
19
+ kind: ToolKind;
20
+ content: ToolCallContent[];
21
+ locations?: ToolCallLocation[];
22
+ }
23
+ interface ToolUpdate {
24
+ title?: string;
25
+ content?: ToolCallContent[];
26
+ locations?: ToolCallLocation[];
27
+ }
28
+ export declare function toolInfoFromToolUse(toolUse: any): ToolInfo;
29
+ export declare function toolUpdateFromToolResult(toolResult: ToolResultBlockParam | BetaToolResultBlockParam | BetaWebSearchToolResultBlockParam | BetaWebFetchToolResultBlockParam | WebSearchToolResultBlockParam | BetaCodeExecutionToolResultBlockParam | BetaBashCodeExecutionToolResultBlockParam | BetaTextEditorCodeExecutionToolResultBlockParam | BetaRequestMCPToolResultBlockParam | BetaToolSearchToolResultBlockParam, toolUse: any | undefined): ToolUpdate;
30
+ export type ClaudePlanEntry = {
31
+ content: string;
32
+ status: "pending" | "in_progress" | "completed";
33
+ activeForm: string;
34
+ };
35
+ export declare function planEntries(input: {
36
+ todos: ClaudePlanEntry[];
37
+ }): PlanEntry[];
38
+ export declare function markdownEscape(text: string): string;
39
+ export declare const registerHookCallback: (toolUseID: string, { onPostToolUseHook, }: {
40
+ onPostToolUseHook?: (toolUseID: string, toolInput: unknown, toolResponse: unknown) => Promise<void>;
41
+ }) => void;
42
+ export declare const createPostToolUseHook: (logger?: Logger, options?: {
43
+ onEnterPlanMode?: () => Promise<void>;
44
+ }) => HookCallback;
45
+ /**
46
+ * Creates a PreToolUse hook that checks permissions using the SettingsManager.
47
+ * This runs before the SDK's built-in permission rules, allowing us to enforce
48
+ * our own permission settings for ACP-prefixed tools.
49
+ */
50
+ export declare const createPreToolUseHook: (settingsManager: SettingsManager, logger?: Logger) => HookCallback;
51
+ export {};
52
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
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"}