mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 20:42:10 +00:00
Compare commits
3 Commits
enable-cla
...
v0.1.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
532daf0e7e | ||
|
|
d6e5910631 | ||
|
|
0d5e78f1ac |
@@ -6,7 +6,7 @@
|
||||
"bin": {
|
||||
"claude-code-acp": "./dist/index.js"
|
||||
},
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.8",
|
||||
"description": "An ACP-compatible coding agent powered by the Claude Code SDK (TypeScript)",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -27,11 +27,15 @@ import {
|
||||
SDKAssistantMessage,
|
||||
SDKUserMessage,
|
||||
} from "@anthropic-ai/claude-code";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as os from "node:os";
|
||||
import { v7 as uuidv7 } from "uuid";
|
||||
import {
|
||||
nodeToWebReadable,
|
||||
nodeToWebWritable,
|
||||
Pushable,
|
||||
sleep,
|
||||
unreachable,
|
||||
} from "./utils.js";
|
||||
import { SessionNotification } from "@zed-industries/agent-client-protocol";
|
||||
@@ -164,7 +168,7 @@ export class ClaudeAcpAgent implements Agent {
|
||||
options.disallowedTools = disallowedTools;
|
||||
}
|
||||
|
||||
let q = query({
|
||||
const q = query({
|
||||
prompt: input,
|
||||
options,
|
||||
});
|
||||
@@ -320,8 +324,20 @@ async function availableSlashCommands(
|
||||
"vim", // Not needed
|
||||
];
|
||||
|
||||
//todo: Do not use `as any` once `supportedCommands` is exposed via the typescript interface
|
||||
const commands = await (query as any).supportedCommands();
|
||||
const commands = await Promise.race([
|
||||
//todo: Do not use `as any` once `supportedCommands` is exposed via the typescript interface
|
||||
(query as any).supportedCommands(),
|
||||
sleep(5000).then(() => {
|
||||
if (
|
||||
fs.existsSync(path.resolve(os.homedir(), ".claude.json.backup")) &&
|
||||
!fs.existsSync(path.resolve(os.homedir(), ".claude.json"))
|
||||
) {
|
||||
throw RequestError.authRequired();
|
||||
}
|
||||
throw new Error("can't load supported slash commands");
|
||||
}),
|
||||
]);
|
||||
|
||||
return commands
|
||||
.map(
|
||||
(command: {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
TerminalOutputResponse,
|
||||
} from "@zed-industries/agent-client-protocol";
|
||||
import { tool } from "@anthropic-ai/claude-code";
|
||||
import { sleep } from "./utils.js";
|
||||
|
||||
export const SYSTEM_REMINDER = `
|
||||
|
||||
@@ -171,9 +172,9 @@ In sessions with mcp__acp__edit always use it instead of Edit as it will
|
||||
allow the user to conveniently review changes.
|
||||
|
||||
File editing instructions:
|
||||
- The \`old_text\` param must match existing file content, including indentation.
|
||||
- The \`old_text\` param must come from the actual file, not an outline.
|
||||
- The \`old_text\` section must not be empty.
|
||||
- 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.
|
||||
@@ -183,10 +184,10 @@ File editing instructions:
|
||||
abs_path: z
|
||||
.string()
|
||||
.describe("The absolute path to the file to read."),
|
||||
old_text: z
|
||||
old_string: z
|
||||
.string()
|
||||
.describe("The old text to replace (must be unique in the file)"),
|
||||
new_text: z.string().describe("The new text."),
|
||||
new_string: z.string().describe("The new text."),
|
||||
},
|
||||
annotations: {
|
||||
title: "Edit file",
|
||||
@@ -219,8 +220,8 @@ File editing instructions:
|
||||
content,
|
||||
[
|
||||
{
|
||||
oldText: input.old_text,
|
||||
newText: input.new_text,
|
||||
oldText: input.old_string,
|
||||
newText: input.new_string,
|
||||
replaceAll: false,
|
||||
},
|
||||
],
|
||||
@@ -598,10 +599,6 @@ File editing instructions:
|
||||
return b.slice(i);
|
||||
}
|
||||
|
||||
function sleep(time: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
function toolCommandOutput(
|
||||
status: "started" | "aborted" | "exited" | "killed" | "timedOut",
|
||||
output: TerminalOutputResponse,
|
||||
|
||||
@@ -309,7 +309,7 @@ describe("tool conversions", () => {
|
||||
id: "toolu_01ABC123XYZ789",
|
||||
name: "Write",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/example.txt",
|
||||
file_path: "/Users/test/project/example.txt",
|
||||
content: "Hello, World!\nThis is test content.",
|
||||
},
|
||||
};
|
||||
@@ -361,7 +361,7 @@ describe("tool conversions", () => {
|
||||
id: "toolu_01MNO456PQR789",
|
||||
name: "Read",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/readme.md",
|
||||
file_path: "/Users/test/project/readme.md",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -664,8 +664,8 @@ describe("tool conversions", () => {
|
||||
name: "edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/example.txt",
|
||||
old_text: "old content",
|
||||
new_text: "new content",
|
||||
old_string: "old content",
|
||||
new_string: "new content",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -795,8 +795,8 @@ describe("tool conversions", () => {
|
||||
name: "mcp__acp__edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/main.py",
|
||||
old_text: "def hello():",
|
||||
new_text: "def hello_world():",
|
||||
old_string: "def hello():",
|
||||
new_string: "def hello_world():",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -823,8 +823,8 @@ describe("tool conversions", () => {
|
||||
name: "edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/notfound.txt",
|
||||
old_text: "nonexistent",
|
||||
new_text: "replacement",
|
||||
old_string: "nonexistent",
|
||||
new_string: "replacement",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -849,8 +849,8 @@ describe("tool conversions", () => {
|
||||
name: "edit",
|
||||
input: {
|
||||
abs_path: "/Users/test/project/test.txt",
|
||||
old_text: "old",
|
||||
new_text: "new",
|
||||
old_string: "old",
|
||||
new_string: "new",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
36
src/tools.ts
36
src/tools.ts
@@ -137,7 +137,7 @@ export function toolInfoFromToolUse(
|
||||
title: "Read File",
|
||||
kind: "read",
|
||||
content: [],
|
||||
locations: [{ path: input.abs_path, line: input.offset ?? 0 }],
|
||||
locations: [{ path: input.file_path, line: input.offset ?? 0 }],
|
||||
};
|
||||
|
||||
case "LS":
|
||||
@@ -149,42 +149,46 @@ export function toolInfoFromToolUse(
|
||||
};
|
||||
|
||||
case "mcp__acp__edit":
|
||||
case "Edit":
|
||||
let oldText = input.old_text || null;
|
||||
let newText = input.new_text;
|
||||
case "Edit": {
|
||||
const path = input?.abs_path ?? input?.file_path;
|
||||
let oldText = input.old_string ?? null;
|
||||
let newText = input.new_string ?? "";
|
||||
let line = null;
|
||||
|
||||
if (input?.abs_path && input.old_text) {
|
||||
if (path && input.old_text) {
|
||||
try {
|
||||
let oldContent = cachedFileContent[input.abs_path] || "";
|
||||
let oldContent = cachedFileContent[path] || "";
|
||||
let newContent = replaceAndCalculateLocation(oldContent, [
|
||||
{
|
||||
oldText: input.old_text,
|
||||
newText: input.new_text,
|
||||
oldText: input.old_string,
|
||||
newText: input.new_string,
|
||||
replaceAll: false,
|
||||
},
|
||||
]);
|
||||
oldText = oldContent;
|
||||
newText = newContent.newContent;
|
||||
line = newContent.lineNumbers[0];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: input?.abs_path ? `Edit ${input.abs_path}` : "Edit",
|
||||
title: path ? `Edit ${path}` : "Edit",
|
||||
kind: "edit",
|
||||
content:
|
||||
input && input.abs_path
|
||||
input && path
|
||||
? [
|
||||
{
|
||||
type: "diff",
|
||||
path: input.abs_path,
|
||||
path,
|
||||
oldText,
|
||||
newText,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
locations: input?.abs_path ? [{ path: input.abs_path }] : [],
|
||||
locations: path ? [{ path, line }] : [],
|
||||
};
|
||||
}
|
||||
|
||||
case "mcp__acp__multi-edit":
|
||||
case "MultiEdit":
|
||||
@@ -265,20 +269,20 @@ export function toolInfoFromToolUse(
|
||||
|
||||
case "Write":
|
||||
return {
|
||||
title: input?.abs_path ? `Write ${input.abs_path}` : "Write",
|
||||
title: input?.file_path ? `Write ${input.file_path}` : "Write",
|
||||
kind: "edit",
|
||||
content:
|
||||
input && input.abs_path
|
||||
input && input.file_path
|
||||
? [
|
||||
{
|
||||
type: "diff",
|
||||
path: input.abs_path,
|
||||
path: input.file_path,
|
||||
oldText: null,
|
||||
newText: input.content,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
locations: input?.abs_path ? [{ path: input.abs_path }] : [],
|
||||
locations: input?.file_path ? [{ path: input.file_path }] : [],
|
||||
};
|
||||
|
||||
case "Glob": {
|
||||
|
||||
@@ -77,3 +77,7 @@ export function nodeToWebReadable(
|
||||
export function unreachable(value: never): never {
|
||||
throw new Error(`Unexpected case: ${value}`);
|
||||
}
|
||||
|
||||
export function sleep(time: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user