Programmatically import and instrument the locally installed Claude Code CLI via Babel AST. Drop-in replacement for @anthropic-ai/claude-agent-sdk that runs in-process -- zero subprocess spawning.
Instead of spawning claude -p as a subprocess (like the official SDK), elwood:
- Locates the installed Claude Code CLI (
cli.js) - Parses the 13MB bundle with Babel AST
- Fingerprints internal functions via evidence-based pattern matching
- Injects instrumentation hooks
- Calls
agentLoop()directly in-process
This gives you the same API as @anthropic-ai/claude-agent-sdk with lower latency and direct access to internals.
npm install @secemp/elwoodRequires Claude Code to be installed:
npm install -g @anthropic-ai/claude-codeimport { query } from '@secemp/elwood';
for await (const message of query({
prompt: 'What files are in this directory?',
options: { maxTurns: 3 },
})) {
if (message.type === 'result') {
console.log(message.result);
}
}No API key needed if logged in via claude login (Pro/Max subscription).
Returns an async generator yielding messages. Options include:
model-- model to usesystemPrompt-- string or{type: 'preset', append: '...'}maxTurns-- maximum conversation turnsallowedTools-- auto-approve these toolshooks--{PreToolUse: [...], PostToolUse: [...], ...}mcpServers-- MCP server configspermissionMode--'default' | 'acceptEdits' | 'bypassPermissions'continue/resume/resumeSessionAt/forkSession-- session managementenv/stderr-- environment and output control- ...and all other official SDK options
const conversation = query({ prompt: '...' });
conversation.interrupt(); // soft interrupt
conversation.setModel('...'); // change model mid-conversation
conversation.setPermissionMode('acceptEdits');
await conversation.supportedModels();
await conversation.supportedCommands();
conversation.close(); // cleanupimport { tool, createSdkMcpServer, query } from '@secemp/elwood';
const weather = tool('get_weather', 'Get weather',
{ type: 'object', properties: { city: { type: 'string' } } },
({ city }) => `72F in ${city}`
);
const server = await createSdkMcpServer({ tools: [weather] });
for await (const msg of query({
prompt: 'Weather in NYC?',
options: { mcpServers: [server] },
})) { /* ... */ }import { listSessions, getSessionMessages, getSessionInfo } from '@secemp/elwood';
const sessions = await listSessions();
const messages = await getSessionMessages(sessions[0].id);import { unstable_v2_createSession, unstable_v2_prompt } from '@secemp/elwood';
const result = await unstable_v2_prompt('Explain recursion');import { locate, load, instrument, parseBundle } from '@secemp/elwood';
const { cliPath, version } = await locate();
const ast = parseBundle(cliPath);Works with all Claude Code auth methods:
- Claude Pro/Max subscription (
claude login) ANTHROPIC_API_KEYenvironment variable- OAuth tokens
- Bedrock/Vertex/Foundry
See the examples/ directory for 24 working examples covering queries, hooks, MCP, custom tools, sessions, and more.
MIT