Skip to content

Commit b72851b

Browse files
committed
feat: set up ACP and Claude adapter
1 parent 960bdd9 commit b72851b

File tree

12 files changed

+3016
-449
lines changed

12 files changed

+3016
-449
lines changed

packages/agent/agent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env node
2+
3+
import { runAcp } from "./src/adapters/claude/claude.js";
4+
5+
runAcp();

packages/agent/example-client.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env node
2+
3+
import { spawn } from "node:child_process";
4+
import { dirname, join } from "node:path";
5+
import * as readline from "node:readline/promises";
6+
import { Readable, Writable } from "node:stream";
7+
import { fileURLToPath } from "node:url";
8+
9+
import {
10+
type Client,
11+
ClientSideConnection,
12+
ndJsonStream,
13+
PROTOCOL_VERSION,
14+
type ReadTextFileRequest,
15+
type ReadTextFileResponse,
16+
type RequestPermissionRequest,
17+
type RequestPermissionResponse,
18+
type SessionNotification,
19+
type WriteTextFileRequest,
20+
type WriteTextFileResponse,
21+
} from "@agentclientprotocol/sdk";
22+
23+
class ExampleClient implements Client {
24+
async requestPermission(
25+
params: RequestPermissionRequest,
26+
): Promise<RequestPermissionResponse> {
27+
console.log(`\n🔐 Permission requested: ${params.toolCall.title}`);
28+
29+
console.log(`\nOptions:`);
30+
params.options.forEach((option, index) => {
31+
console.log(` ${index + 1}. ${option.name} (${option.kind})`);
32+
});
33+
34+
while (true) {
35+
const rl = readline.createInterface({
36+
input: process.stdin,
37+
output: process.stdout,
38+
});
39+
40+
const answer = await rl.question("\nChoose an option: ");
41+
const trimmedAnswer = answer.trim();
42+
rl.close();
43+
44+
const optionIndex = parseInt(trimmedAnswer, 10) - 1;
45+
if (optionIndex >= 0 && optionIndex < params.options.length) {
46+
return {
47+
outcome: {
48+
outcome: "selected",
49+
optionId: params.options[optionIndex].optionId,
50+
},
51+
};
52+
}
53+
console.log("Invalid option. Please try again.");
54+
}
55+
}
56+
57+
async sessionUpdate(params: SessionNotification): Promise<void> {
58+
const update = params.update;
59+
60+
switch (update.sessionUpdate) {
61+
case "agent_message_chunk":
62+
if (update.content.type === "text") {
63+
process.stdout.write(update.content.text);
64+
} else {
65+
console.log(`[${update.content.type}]`);
66+
}
67+
break;
68+
case "tool_call":
69+
console.log(`\n🔧 ${update.title} (${update.status})`);
70+
break;
71+
case "tool_call_update":
72+
console.log(
73+
`\n🔧 Tool call \`${update.toolCallId}\` updated: ${update.status}\n`,
74+
);
75+
break;
76+
case "plan":
77+
case "agent_thought_chunk":
78+
case "user_message_chunk":
79+
console.log(`[${update.sessionUpdate}]`);
80+
break;
81+
default:
82+
break;
83+
}
84+
}
85+
86+
async writeTextFile(
87+
params: WriteTextFileRequest,
88+
): Promise<WriteTextFileResponse> {
89+
console.error(
90+
"[Client] Write text file called with:",
91+
JSON.stringify(params, null, 2),
92+
);
93+
94+
return {};
95+
}
96+
97+
async readTextFile(
98+
params: ReadTextFileRequest,
99+
): Promise<ReadTextFileResponse> {
100+
console.error(
101+
"[Client] Read text file called with:",
102+
JSON.stringify(params, null, 2),
103+
);
104+
105+
return {
106+
content: "Mock file content",
107+
};
108+
}
109+
}
110+
111+
async function main() {
112+
const __filename = fileURLToPath(import.meta.url);
113+
const __dirname = dirname(__filename);
114+
const agentPath = join(__dirname, "agent.ts");
115+
116+
// Spawn the agent as a subprocess using tsx
117+
const agentProcess = spawn("npx", ["tsx", agentPath], {
118+
stdio: ["pipe", "pipe", "inherit"],
119+
});
120+
121+
// Create streams to communicate with the agent
122+
const input = Writable.toWeb(agentProcess.stdin!);
123+
const output = Readable.toWeb(
124+
agentProcess.stdout!,
125+
) as unknown as ReadableStream<Uint8Array>;
126+
127+
// Create the client connection
128+
const client = new ExampleClient();
129+
const stream = ndJsonStream(input, output);
130+
const connection = new ClientSideConnection((_agent) => client, stream);
131+
132+
try {
133+
// Initialize the connection
134+
const initResult = await connection.initialize({
135+
protocolVersion: PROTOCOL_VERSION,
136+
clientCapabilities: {
137+
fs: {
138+
readTextFile: true,
139+
writeTextFile: true,
140+
},
141+
},
142+
});
143+
144+
console.log(
145+
`✅ Connected to agent (protocol v${initResult.protocolVersion})`,
146+
);
147+
148+
// Create a new session
149+
const sessionResult = await connection.newSession({
150+
cwd: process.cwd(),
151+
mcpServers: [],
152+
});
153+
154+
console.log(`📝 Created session: ${sessionResult.sessionId}`);
155+
console.log(`💬 User: Hello, agent!\n`);
156+
157+
// Send a test prompt
158+
const promptResult = await connection.prompt({
159+
sessionId: sessionResult.sessionId,
160+
prompt: [
161+
{
162+
type: "text",
163+
text: "Hello, agent!",
164+
},
165+
],
166+
});
167+
168+
console.log(`\n\n✅ Agent completed with: ${promptResult.stopReason}`);
169+
} catch (error) {
170+
console.error("[Client] Error:", error);
171+
} finally {
172+
agentProcess.kill();
173+
process.exit(0);
174+
}
175+
}
176+
177+
main().catch(console.error);

packages/agent/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Main entry point - re-exports from src
22

3-
export { ClaudeAdapter } from "./src/adapters/claude/claude-adapter.js";
4-
// Provider adapter types
5-
export type { ProviderAdapter } from "./src/adapters/types.js";
6-
export { Agent } from "./src/agent.js";
3+
// TODO: Refactor - legacy adapter removed
4+
// export { ClaudeAdapter } from "./src/adapters/claude-legacy/claude-adapter.js";
5+
// export type { ProviderAdapter } from "./src/adapters/types.js";
6+
// export { Agent } from "./src/agent.js";
77
export type { TodoItem, TodoList } from "./src/todo-manager.js";
88
// Todo management
99
export { TodoManager } from "./src/todo-manager.js";

packages/agent/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@
5151
"typescript": "^5.5.0"
5252
},
5353
"dependencies": {
54-
"@anthropic-ai/claude-agent-sdk": "^0.1.47",
54+
"@agentclientprotocol/sdk": "^0.5.1",
55+
"@anthropic-ai/claude-agent-sdk": "^0.1.55",
56+
"@anthropic-ai/sdk": "^0.71.0",
57+
"@modelcontextprotocol/sdk": "^1.23.0",
58+
"diff": "^8.0.2",
5559
"dotenv": "^17.2.3",
60+
"uuid": "13.0.0",
5661
"yoga-wasm-web": "^0.3.3",
5762
"zod": "^4.1.12"
5863
},

0 commit comments

Comments
 (0)