// src/index.ts
import Anthropic from "@anthropic-ai/sdk";
import * as fs from "fs";
import { execSync } from "child_process";
const client = new Anthropic();
// Read CLAUDE.md for system instructions
const systemPrompt = fs.existsSync("CLAUDE.md")
? fs.readFileSync("CLAUDE.md", "utf-8")
: "You are a helpful assistant.";
// Define tools
const tools: Anthropic.Tool[] = [
{
name: "read_file",
description: "Read the contents of a file",
input_schema: {
type: "object",
properties: {
path: { type: "string", description: "Path to the file" }
},
required: ["path"]
}
},
{
name: "write_file",
description: "Write content to a file",
input_schema: {
type: "object",
properties: {
path: { type: "string", description: "Path to the file" },
content: { type: "string", description: "Content to write" }
},
required: ["path", "content"]
}
},
{
name: "bash",
description: "Execute a bash command",
input_schema: {
type: "object",
properties: {
command: { type: "string", description: "Command to execute" }
},
required: ["command"]
}
}
];
// Handle tool execution
async function executeTool(name: string, input: any): Promise<string> {
switch (name) {
case "read_file":
return fs.readFileSync(input.path, "utf-8");
case "write_file":
fs.writeFileSync(input.path, input.content);
return `Wrote ${input.content.length} bytes to ${input.path}`;
case "bash":
return execSync(input.command, { encoding: "utf-8" });
default:
throw new Error(`Unknown tool: ${name}`);
}
}
// Agent loop
async function runAgent(prompt: string): Promise<string> {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: prompt }
];
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 4096,
system: systemPrompt,
tools,
messages
});
// Collect text and tool uses
let textResponse = "";
const toolUses: Anthropic.ToolUseBlock[] = [];
for (const block of response.content) {
if (block.type === "text") {
textResponse += block.text;
} else if (block.type === "tool_use") {
toolUses.push(block);
}
}
// If no tool uses, we're done
if (toolUses.length === 0) {
return textResponse;
}
// Execute tools and continue
messages.push({ role: "assistant", content: response.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const toolUse of toolUses) {
try {
const result = await executeTool(toolUse.name, toolUse.input);
toolResults.push({
type: "tool_result",
tool_use_id: toolUse.id,
content: result
});
} catch (error) {
toolResults.push({
type: "tool_result",
tool_use_id: toolUse.id,
content: `Error: ${error.message}`,
is_error: true
});
}
}
messages.push({ role: "user", content: toolResults });
}
}
// Entry point: read from stdin, write to stdout
async function main() {
let prompt = "";
for await (const chunk of process.stdin) {
prompt += chunk;
}
const response = await runAgent(prompt.trim());
console.log(response);
}
main().catch(console.error);