mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 12:40:32 +00:00
Compare commits
5 Commits
glob-backt
...
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4c9ba3c7a | ||
|
|
a08ce88f30 | ||
|
|
f717175cdc | ||
|
|
d1fd794ecb | ||
|
|
f54da50bf5 |
@@ -26,7 +26,7 @@ To use Claude Code, open the Agent Panel and click "New Claude Code Thread" from
|
||||
|
||||
https://github.com/user-attachments/assets/ddce66c7-79ac-47a3-ad59-4a6a3ca74903
|
||||
|
||||
[Read the docs](https://zed.dev/docs/ai/external-agents) on External Agent support.
|
||||
Read the docs on [External Agent](https://zed.dev/docs/ai/external-agents) support.
|
||||
|
||||
### Other clients
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "@zed-industries/claude-code-acp",
|
||||
"version": "0.1.11",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@zed-industries/claude-code-acp",
|
||||
"version": "0.1.11",
|
||||
"version": "0.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.100",
|
||||
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||
"@types/express": "^5.0.3",
|
||||
"@zed-industries/agent-client-protocol": "0.2.0-alpha.4",
|
||||
"diff": "^8.0.2",
|
||||
"express": "^5.1.0",
|
||||
"minimist": "^1.2.8",
|
||||
"uuid": "11.1.0"
|
||||
@@ -2215,10 +2216,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",
|
||||
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
@@ -4362,6 +4362,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node/node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"bin": {
|
||||
"claude-code-acp": "./dist/index.js"
|
||||
},
|
||||
"version": "0.1.11",
|
||||
"version": "0.2.0",
|
||||
"description": "An ACP-compatible coding agent powered by the Claude Code SDK (TypeScript)",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
@@ -47,6 +47,7 @@
|
||||
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||
"@types/express": "^5.0.3",
|
||||
"@zed-industries/agent-client-protocol": "0.2.0-alpha.4",
|
||||
"diff": "^8.0.2",
|
||||
"express": "^5.1.0",
|
||||
"minimist": "^1.2.8",
|
||||
"uuid": "11.1.0"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { z } from "zod";
|
||||
import { Server } from "node:http";
|
||||
import { ClaudeAcpAgent } from "./acp-agent.js";
|
||||
import { ClientCapabilities, TerminalOutputResponse } from "@zed-industries/agent-client-protocol";
|
||||
import * as diff from "diff";
|
||||
|
||||
import { sleep, unreachable } from "./utils.js";
|
||||
|
||||
@@ -167,7 +168,8 @@ File editing instructions:
|
||||
- For unique lines, include only those lines.
|
||||
- For non-unique lines, include enough context to identify them.
|
||||
- Do not escape quotes, newlines, or other characters.
|
||||
- Only edit the specified file.`,
|
||||
- Only edit the specified file.
|
||||
- If the \`old_string\` value isn't found in the file, the edit won't be applied. The tool will fail and must be retried.`,
|
||||
inputSchema: {
|
||||
abs_path: z.string().describe("The absolute path to the file to read."),
|
||||
old_string: z.string().describe("The old text to replace (must be unique in the file)"),
|
||||
@@ -182,56 +184,47 @@ File editing instructions:
|
||||
},
|
||||
},
|
||||
async (input) => {
|
||||
try {
|
||||
const session = agent.sessions[sessionId];
|
||||
if (!session) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "The user has left the building",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const { content } = await agent.readTextFile({
|
||||
sessionId,
|
||||
path: input.abs_path,
|
||||
});
|
||||
|
||||
const { newContent, lineNumbers } = replaceAndCalculateLocation(content, [
|
||||
{
|
||||
oldText: input.old_string,
|
||||
newText: input.new_string,
|
||||
replaceAll: false,
|
||||
},
|
||||
]);
|
||||
|
||||
await agent.writeTextFile({
|
||||
sessionId,
|
||||
path: input.abs_path,
|
||||
content: newContent,
|
||||
});
|
||||
|
||||
const session = agent.sessions[sessionId];
|
||||
if (!session) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers }),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Editing file failed: " + error.message,
|
||||
text: "The user has left the building",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const { content } = await agent.readTextFile({
|
||||
sessionId,
|
||||
path: input.abs_path,
|
||||
});
|
||||
|
||||
const { newContent } = replaceAndCalculateLocation(content, [
|
||||
{
|
||||
oldText: input.old_string,
|
||||
newText: input.new_string,
|
||||
replaceAll: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const patch = diff.createPatch(input.abs_path, content, newContent);
|
||||
|
||||
await agent.writeTextFile({
|
||||
sessionId,
|
||||
path: input.abs_path,
|
||||
content: newContent,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: patch,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -239,7 +232,20 @@ File editing instructions:
|
||||
"multi-edit",
|
||||
{
|
||||
title: "Multi Edit",
|
||||
description: `Edit a file with multiple sequential edits.`,
|
||||
description: `Edit a file with multiple sequential edits.
|
||||
|
||||
In sessions with mcp__acp__multi-edit always use it instead of MultiEdit as it will
|
||||
allow the user to conveniently review changes.
|
||||
|
||||
File editing instructions:
|
||||
- The \`old_string\` param must match existing file content, including indentation.
|
||||
- The \`old_string\` param must come from the actual file, not an outline.
|
||||
- The \`old_string\` section must not be empty.
|
||||
- Be minimal with replacements:
|
||||
- For unique lines, include only those lines.
|
||||
- For non-unique lines, include enough context to identify them, unless you're using \`replace_all\`
|
||||
- Do not escape quotes, newlines, or other characters.
|
||||
- If any of the provided \`old_string\` values aren't found in the file, no edits will be applied. The tool will fail and must be retried.`,
|
||||
inputSchema: {
|
||||
file_path: z.string().describe("The absolute path to the file to modify"),
|
||||
edits: z
|
||||
@@ -282,7 +288,7 @@ File editing instructions:
|
||||
path: input.file_path,
|
||||
});
|
||||
|
||||
const { newContent, lineNumbers } = replaceAndCalculateLocation(
|
||||
const { newContent } = replaceAndCalculateLocation(
|
||||
content,
|
||||
input.edits.map((edit) => ({
|
||||
oldText: edit.old_string,
|
||||
@@ -291,6 +297,8 @@ File editing instructions:
|
||||
})),
|
||||
);
|
||||
|
||||
const patch = diff.createPatch(input.file_path, content, newContent);
|
||||
|
||||
await agent.writeTextFile({
|
||||
sessionId,
|
||||
path: input.file_path,
|
||||
@@ -301,7 +309,7 @@ File editing instructions:
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers }),
|
||||
text: patch,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -771,7 +779,7 @@ export function replaceAndCalculateLocation(
|
||||
for (const edit of edits) {
|
||||
// Skip empty oldText
|
||||
if (edit.oldText === "") {
|
||||
continue;
|
||||
throw new Error(`The provided \`old_string\` is empty.\n\nNo edits were applied.`);
|
||||
}
|
||||
|
||||
if (edit.replaceAll) {
|
||||
@@ -783,6 +791,11 @@ export function replaceAndCalculateLocation(
|
||||
while (true) {
|
||||
const index = currentContent.indexOf(edit.oldText, searchIndex);
|
||||
if (index === -1) {
|
||||
if (searchIndex === 0) {
|
||||
throw new Error(
|
||||
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".\n\nNo edits were applied.`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -804,7 +817,11 @@ export function replaceAndCalculateLocation(
|
||||
} else {
|
||||
// Replace first occurrence only
|
||||
const index = currentContent.indexOf(edit.oldText);
|
||||
if (index !== -1) {
|
||||
if (index === -1) {
|
||||
throw new Error(
|
||||
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".\n\nNo edits were applied.`,
|
||||
);
|
||||
} else {
|
||||
const marker = `${markerPrefix}${markerCounter++}__`;
|
||||
markers.push(marker);
|
||||
currentContent =
|
||||
|
||||
@@ -638,36 +638,6 @@ describe("tool conversions", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("should extract locations from edit tool results", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01ABC123",
|
||||
name: "edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/example.txt",
|
||||
old_string: "old content",
|
||||
new_string: "new content",
|
||||
},
|
||||
};
|
||||
|
||||
const toolResult = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers: [5, 10, 15] }),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
|
||||
expect(update.locations).toEqual([
|
||||
{ path: "/Users/test/project/example.txt", line: 5 },
|
||||
{ path: "/Users/test/project/example.txt", line: 10 },
|
||||
{ path: "/Users/test/project/example.txt", line: 15 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should show full diff for multi edit tool", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
@@ -728,104 +698,11 @@ describe("tool conversions", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should extract locations from multi-edit tool results", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01DEF456",
|
||||
name: "multi-edit",
|
||||
input: {
|
||||
file_path: "/Users/test/project/config.json",
|
||||
edits: [
|
||||
{
|
||||
old_string: "version: 1.0",
|
||||
new_string: "version: 2.0",
|
||||
replace_all: false,
|
||||
},
|
||||
{
|
||||
old_string: "enabled: false",
|
||||
new_string: "enabled: true",
|
||||
replace_all: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const toolResult = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers: [2, 8, 12, 20] }),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
|
||||
expect(update.locations).toEqual([
|
||||
{ path: "/Users/test/project/config.json", line: 2 },
|
||||
{ path: "/Users/test/project/config.json", line: 8 },
|
||||
{ path: "/Users/test/project/config.json", line: 12 },
|
||||
{ path: "/Users/test/project/config.json", line: 20 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should handle mcp__acp__edit tool results", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01GHI789",
|
||||
name: "mcp__acp__edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/main.py",
|
||||
old_string: "def hello():",
|
||||
new_string: "def hello_world():",
|
||||
},
|
||||
};
|
||||
|
||||
const toolResult = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers: [42] }),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
|
||||
expect(update.locations).toEqual([{ path: "/Users/test/project/main.py", line: 42 }]);
|
||||
});
|
||||
|
||||
it("should handle empty locations from edit tools", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01JKL012",
|
||||
name: "edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/notfound.txt",
|
||||
old_string: "nonexistent",
|
||||
new_string: "replacement",
|
||||
},
|
||||
};
|
||||
|
||||
const toolResult = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ lineNumbers: [] }),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
|
||||
expect(update.locations).toEqual([]);
|
||||
});
|
||||
|
||||
it("should handle malformed tool results gracefully", () => {
|
||||
it("should return empty update for successful edit result", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01MNO345",
|
||||
name: "edit",
|
||||
name: "mcp__acp__edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/test.txt",
|
||||
old_string: "old",
|
||||
@@ -840,6 +717,9 @@ describe("tool conversions", () => {
|
||||
text: "not valid json",
|
||||
},
|
||||
],
|
||||
tool_use_id: "test",
|
||||
is_error: false,
|
||||
type: "tool_result" as const,
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
@@ -847,6 +727,40 @@ describe("tool conversions", () => {
|
||||
// Should return empty object when parsing fails
|
||||
expect(update).toEqual({});
|
||||
});
|
||||
|
||||
it("should return content update for edit failure", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_01MNO345",
|
||||
name: "mcp__acp__edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/test.txt",
|
||||
old_string: "old",
|
||||
new_string: "new",
|
||||
},
|
||||
};
|
||||
|
||||
const toolResult = {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Failed to find `old_string`",
|
||||
},
|
||||
],
|
||||
tool_use_id: "test",
|
||||
is_error: true,
|
||||
type: "tool_result" as const,
|
||||
};
|
||||
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse);
|
||||
|
||||
// Should return empty object when parsing fails
|
||||
expect(update).toEqual({
|
||||
content: [
|
||||
{ content: { type: "text", text: "Failed to find `old_string`" }, type: "content" },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("escape markdown", () => {
|
||||
|
||||
@@ -22,12 +22,9 @@ describe("replaceAndCalculateLocation", () => {
|
||||
|
||||
it("should return empty line numbers array when no match found", () => {
|
||||
const content = "line 1\nline 2\nline 3";
|
||||
const result = replaceAndCalculateLocation(content, [
|
||||
{ oldText: "notfound", newText: "replaced" },
|
||||
]);
|
||||
|
||||
expect(result.newContent).toBe(content);
|
||||
expect(result.lineNumbers).toEqual([]);
|
||||
expect(() => {
|
||||
replaceAndCalculateLocation(content, [{ oldText: "notfound", newText: "replaced" }]);
|
||||
}).toThrow('The provided `old_string` does not appear in the file: "notfound"');
|
||||
});
|
||||
|
||||
it("should handle Windows line endings (CRLF)", () => {
|
||||
@@ -89,35 +86,31 @@ describe("replaceAndCalculateLocation", () => {
|
||||
});
|
||||
|
||||
it("should handle empty file content", () => {
|
||||
const content = "";
|
||||
const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]);
|
||||
|
||||
expect(result.newContent).toBe("");
|
||||
expect(result.lineNumbers).toEqual([]);
|
||||
expect(() => {
|
||||
replaceAndCalculateLocation("", [{ oldText: "text", newText: "replaced" }]);
|
||||
}).toThrowError(
|
||||
'The provided `old_string` does not appear in the file: "text".\n\nNo edits were applied.',
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle empty search text", () => {
|
||||
// Test with replaceAll false (default)
|
||||
const content1 = "line 1\nline 2\nline 3";
|
||||
const result1 = replaceAndCalculateLocation(content1, [{ oldText: "", newText: "replaced" }]);
|
||||
expect(result1.newContent).toBe(content1);
|
||||
expect(result1.lineNumbers).toEqual([]);
|
||||
expect(() => {
|
||||
replaceAndCalculateLocation(content1, [{ oldText: "", newText: "replaced" }]);
|
||||
}).toThrowError("The provided `old_string` is empty.\n\nNo edits were applied.");
|
||||
|
||||
// Test with replaceAll true on single line
|
||||
const content2 = "abc";
|
||||
const result2 = replaceAndCalculateLocation(content2, [
|
||||
{ oldText: "", newText: "X", replaceAll: true },
|
||||
]);
|
||||
expect(result2.newContent).toBe(content2);
|
||||
expect(result2.lineNumbers).toEqual([]);
|
||||
expect(() => {
|
||||
replaceAndCalculateLocation(content2, [{ oldText: "", newText: "X", replaceAll: true }]);
|
||||
}).toThrowError("The provided `old_string` is empty.\n\nNo edits were applied.");
|
||||
|
||||
// Test with replaceAll true on multiline content
|
||||
const content3 = "ab\ncd";
|
||||
const result3 = replaceAndCalculateLocation(content3, [
|
||||
{ oldText: "", newText: "X", replaceAll: true },
|
||||
]);
|
||||
expect(result3.newContent).toBe(content3);
|
||||
expect(result3.lineNumbers).toEqual([]);
|
||||
expect(() => {
|
||||
replaceAndCalculateLocation(content3, [{ oldText: "", newText: "X", replaceAll: true }]);
|
||||
}).toThrowError("The provided `old_string` is empty.\n\nNo edits were applied.");
|
||||
});
|
||||
|
||||
it("should handle single line content", () => {
|
||||
|
||||
124
src/tools.ts
124
src/tools.ts
@@ -424,60 +424,18 @@ export function toolInfoFromToolUse(
|
||||
}
|
||||
}
|
||||
|
||||
export function toolUpdateFromToolResult(toolResult: any, toolUse: any | undefined): ToolUpdate {
|
||||
type ToolResult = {
|
||||
type: "tool_result";
|
||||
content: any;
|
||||
tool_use_id: string;
|
||||
is_error: boolean;
|
||||
};
|
||||
|
||||
export function toolUpdateFromToolResult(
|
||||
toolResult: ToolResult,
|
||||
toolUse: any | undefined,
|
||||
): ToolUpdate {
|
||||
switch (toolUse?.name) {
|
||||
case "mcp__acp__edit":
|
||||
case "edit":
|
||||
case "Edit": {
|
||||
// Parse the line numbers from the tool result
|
||||
if (toolResult.content && toolResult.content.length > 0) {
|
||||
try {
|
||||
const firstContent = toolResult.content[0];
|
||||
const data = JSON.parse(firstContent.text || firstContent);
|
||||
if (data.lineNumbers && Array.isArray(data.lineNumbers)) {
|
||||
const locations: ToolCallLocation[] = data.lineNumbers.map((line: number) => ({
|
||||
path: toolUse?.input?.abs_path || toolUse?.input?.file_path,
|
||||
line: line,
|
||||
}));
|
||||
return { locations };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error parsing edit tool result: ${error}`);
|
||||
// If parsing fails, return empty object
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case "mcp__acp__multi-edit":
|
||||
case "multi-edit":
|
||||
case "MultiEdit": {
|
||||
// Parse the line numbers from the tool result
|
||||
if (toolResult.content && toolResult.content.length > 0) {
|
||||
try {
|
||||
const firstContent = toolResult.content[0];
|
||||
const data = JSON.parse(firstContent.text || firstContent);
|
||||
if (data.lineNumbers && Array.isArray(data.lineNumbers)) {
|
||||
const locations: ToolCallLocation[] = data.lineNumbers.map((line: number) => ({
|
||||
path: toolUse?.input?.file_path || toolUse?.input?.abs_path,
|
||||
line: line,
|
||||
}));
|
||||
return { locations };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error parsing edit tool result: ${error}`);
|
||||
|
||||
// If parsing fails, return empty object
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
case "mcp__acp__write":
|
||||
case "Write":
|
||||
return {};
|
||||
|
||||
case "Read":
|
||||
case "mcp__acp__read":
|
||||
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
||||
@@ -509,8 +467,20 @@ export function toolUpdateFromToolResult(toolResult: any, toolUse: any | undefin
|
||||
return {};
|
||||
|
||||
case "mcp__acp__Bash":
|
||||
// The bash tool already embeds a terminal
|
||||
case "mcp__acp__edit":
|
||||
case "edit":
|
||||
case "Edit":
|
||||
case "mcp__acp__multi-edit":
|
||||
case "multi-edit":
|
||||
case "MultiEdit":
|
||||
case "mcp__acp__write":
|
||||
case "Write": {
|
||||
if (toolResult.is_error && toolResult.content?.length > 0) {
|
||||
// Only return errors
|
||||
return toAcpContentUpdate(toolResult.content);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
case "Task":
|
||||
case "NotebookEdit":
|
||||
@@ -527,31 +497,35 @@ export function toolUpdateFromToolResult(toolResult: any, toolUse: any | undefin
|
||||
case "WebSearch":
|
||||
case "Other":
|
||||
default: {
|
||||
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
||||
return {
|
||||
content: toolResult.content.map((content: any) => ({
|
||||
type: "content",
|
||||
content,
|
||||
})),
|
||||
};
|
||||
} else if (typeof toolResult.content === "string" && toolResult.content.length > 0) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "content",
|
||||
content: {
|
||||
type: "text",
|
||||
text: toolResult.content,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
return toAcpContentUpdate(toolResult.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toAcpContentUpdate(content: any): { content?: ToolCallContent[] } {
|
||||
if (Array.isArray(content) && content.length > 0) {
|
||||
return {
|
||||
content: content.map((content: any) => ({
|
||||
type: "content",
|
||||
content,
|
||||
})),
|
||||
};
|
||||
} else if (typeof content === "string" && content.length > 0) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "content",
|
||||
content: {
|
||||
type: "text",
|
||||
text: content,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
type ClaudePlanEntry = {
|
||||
content: string;
|
||||
status: "pending" | "in_progress" | "completed";
|
||||
|
||||
Reference in New Issue
Block a user