mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 04:30:59 +00:00
Render read files in code blocks
This commit is contained in:
@@ -6,6 +6,12 @@ import { z } from "zod";
|
||||
import { Server } from "node:http";
|
||||
import { ClaudeAcpAgent } from "./acp-agent.js";
|
||||
|
||||
export const SYSTEM_REMINDER = `
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.
|
||||
</system-reminder>`;
|
||||
|
||||
export function createMcpServer(
|
||||
agent: ClaudeAcpAgent,
|
||||
sessionId: string,
|
||||
@@ -70,13 +76,7 @@ In sessions with mcp__acp__read always use it instead of Read as it contains the
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text:
|
||||
content.content +
|
||||
`
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.
|
||||
</system-reminder>`,
|
||||
text: content.content + SYSTEM_REMINDER,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
WriteTextFileResponse,
|
||||
} from "@zed-industries/agent-client-protocol";
|
||||
import { nodeToWebWritable, nodeToWebReadable } from "../utils.js";
|
||||
import { toolInfoFromToolUse, toolUpdateFromToolResult } from "../tools.js";
|
||||
import {
|
||||
markdownEscape,
|
||||
toolInfoFromToolUse,
|
||||
toolUpdateFromToolResult,
|
||||
} from "../tools.js";
|
||||
import { toAcpNotifications } from "../acp-agent.js";
|
||||
import { SDKAssistantMessage } from "@anthropic-ai/claude-code";
|
||||
|
||||
@@ -776,3 +780,17 @@ describe("tool conversions", () => {
|
||||
expect(update).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("escape markdown", () => {
|
||||
it("should escape markdown characters", () => {
|
||||
let text = "Hello *world*!";
|
||||
let escaped = markdownEscape(text);
|
||||
expect(escaped).toEqual("```\nHello *world*!\n```");
|
||||
|
||||
text = "for example:\n```markdown\nHello *world*!\n```\n";
|
||||
escaped = markdownEscape(text);
|
||||
expect(escaped).toEqual(
|
||||
"````\nfor example:\n```markdown\nHello *world*!\n```\n````",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
59
src/tools.ts
59
src/tools.ts
@@ -6,7 +6,8 @@ import {
|
||||
ToolCallLocation,
|
||||
ToolKind,
|
||||
} from "@zed-industries/agent-client-protocol";
|
||||
import { replaceAndCalculateLocation } from "./mcp-server.js";
|
||||
import { replaceAndCalculateLocation, SYSTEM_REMINDER } from "./mcp-server.js";
|
||||
import { tool } from "@anthropic-ai/claude-code";
|
||||
|
||||
interface ToolInfo {
|
||||
title: string;
|
||||
@@ -489,6 +490,43 @@ export function toolUpdateFromToolResult(
|
||||
case "Write":
|
||||
return {};
|
||||
|
||||
case "Read":
|
||||
case "mcp__acp__read":
|
||||
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
||||
return {
|
||||
content: toolResult.content.map((content: any) => ({
|
||||
type: "content",
|
||||
content:
|
||||
content.type == "text"
|
||||
? {
|
||||
type: "text",
|
||||
text: markdownEscape(
|
||||
content.text.replace(SYSTEM_REMINDER, ""),
|
||||
),
|
||||
}
|
||||
: content,
|
||||
})),
|
||||
};
|
||||
} else if (
|
||||
typeof toolResult.content === "string" &&
|
||||
toolResult.content.length > 0
|
||||
) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "content",
|
||||
content: {
|
||||
type: "text",
|
||||
text: markdownEscape(
|
||||
toolResult.content.replace(SYSTEM_REMINDER, ""),
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
|
||||
case "Task":
|
||||
case "NotebookEdit":
|
||||
case "NotebookRead":
|
||||
@@ -502,12 +540,8 @@ export function toolUpdateFromToolResult(
|
||||
case "Grep":
|
||||
case "WebFetch":
|
||||
case "WebSearch":
|
||||
case "Read":
|
||||
case "mcp__acp__read":
|
||||
case "Other":
|
||||
default: {
|
||||
// This happens for the mcp__acp__read tool,
|
||||
// but may also for others...
|
||||
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
||||
return {
|
||||
content: toolResult.content.map((content: any) => ({
|
||||
@@ -523,7 +557,10 @@ export function toolUpdateFromToolResult(
|
||||
content: [
|
||||
{
|
||||
type: "content",
|
||||
content: { type: "text", text: toolResult.content },
|
||||
content: {
|
||||
type: "text",
|
||||
text: toolResult.content,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -546,3 +583,13 @@ export function planEntries(input: { todos: ClaudePlanEntry[] }): PlanEntry[] {
|
||||
priority: "medium",
|
||||
}));
|
||||
}
|
||||
|
||||
export function markdownEscape(text: string): string {
|
||||
let escape = "```";
|
||||
for (let [m] of text.matchAll(/^```+/gm)) {
|
||||
while (m.length >= escape.length) {
|
||||
escape += "`";
|
||||
}
|
||||
}
|
||||
return escape + "\n" + text + (text.endsWith("\n") ? "" : "\n") + escape;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user