claude-code-wrapper
v1.0.1
Published
Node.js wrapper for the Claude Code CLI — process management, event parsing, session continuity, and permission relay
Downloads
28
Maintainers
Readme
claude-code-wrapper
Node.js wrapper for the Claude Code CLI — build internal desktop apps powered by Claude Code.
This library is for developers building their own desktop applications that use Claude Code as the inference engine. It is not a third-party harness or proxy — it wraps your local Claude Code CLI installation, using your own API key and configuration, to give you programmatic control from Node.js.
Use it when you want to embed Claude Code into an Electron app, a local dev tool, an internal workflow, or anything else where you need structured access to Claude Code's streaming output, tool use, and multi-turn sessions.
Under the hood it manages: NDJSON stream parsing, incremental content assembly, multi-turn session continuity via --resume, permission request relay, and the many edge cases that surface in production (compaction mid-stream, partial JSON tool inputs, non-streaming fallback, flush-on-close). Born from the Synapse distributed agent swarm system, where dozens of concurrent Claude Code processes need rock-solid programmatic control.
Install
npm install claude-code-wrapperQuick Start
One-shot prompt, await the result:
import { createSession } from 'claude-code-wrapper';
const session = createSession({ cwd: '/path/to/project' });
const turn = session.send('Explain what this project does in two sentences.');
const result = await turn.result;
console.log(result.text);
console.log(`Cost: $${result.cost}`);Streaming
Subscribe to events for real-time output:
const session = createSession({ cwd: './my-project' });
const turn = session.send('Refactor the auth module to use JWT.');
turn.on('text', (chunk) => process.stdout.write(chunk));
turn.on('thinking', (chunk) => process.stderr.write(`[thinking] ${chunk}\n`));
turn.on('tool_use', (tool) => console.log(`Tool: ${tool.name}`, tool.input));
turn.on('tool_result', (res) => console.log(`Result for ${res.toolUseId}`));
turn.on('error', (err) => console.error(err.message));
const result = await turn.result;
console.log(`\nDone. Exit code: ${result.exitCode}`);Multi-Turn
Each send() call automatically resumes the same session via --resume:
const session = createSession({ cwd: './my-project' });
const t1 = session.send('Read the README and summarize the architecture.');
await t1.result;
// Continues the same conversation — context is preserved
const t2 = session.send('Now add a /health endpoint based on that architecture.');
const result = await t2.result;
console.log(result.text);
console.log(`Session: ${session.sessionId}`);Permission Handling
Three modes for controlling tool-use permissions:
Auto-approve everything
const session = createSession({
cwd: './my-project',
permissions: 'auto',
});Deny all permission requests (skip)
const session = createSession({
cwd: './my-project',
permissions: 'skip',
});Prompt — handle each request programmatically
const session = createSession({
cwd: './my-project',
permissions: 'prompt',
});
const turn = session.send('Delete all .tmp files in this directory.');
turn.on('permission', (request, respond) => {
console.log(`Tool "${request.toolName}" wants to run:`, request.input);
if (request.toolName === 'Bash' && /rm|delete/i.test(JSON.stringify(request.input))) {
respond('deny');
} else {
respond('allow');
}
});
await turn.result;Parallel Workers
Spin up independent sessions for concurrent workloads:
const tasks = [
{ cwd: './api', prompt: 'Add input validation to all POST endpoints.' },
{ cwd: './web', prompt: 'Convert class components to functional components.' },
{ cwd: './docs', prompt: 'Generate API reference from JSDoc comments.' },
];
const results = await Promise.all(
tasks.map(({ cwd, prompt }) => {
const session = createSession({ cwd, permissions: 'auto' });
return session.send(prompt).result;
})
);
for (const r of results) {
console.log(`Exit ${r.exitCode} | Cost: $${r.cost} | ${r.text.slice(0, 80)}...`);
}API Overview
Functions
| Function | Description |
|---|---|
| createSession(opts) | Create a new CLI session. Returns a Session object. |
Session
| Property / Method | Description |
|---|---|
| session.send(prompt) | Send a prompt. Returns a Turn with events and .result. |
| session.abort() | Kill the running CLI process. |
| session.sessionId | The CLI session ID (populated after first send()). |
| session.isActive | true while a turn is in progress. |
Session Options
| Option | Type | Default | Description |
|---|---|---|---|
| cwd | string | process.cwd() | Working directory for the CLI process. |
| model | string | CLI default | Model to use (e.g. claude-sonnet-4-20250514). |
| cli | string | 'claude' | Path to the Claude Code CLI binary. |
| permissions | 'prompt' \| 'skip' \| 'auto' | 'prompt' | How to handle tool-use permission requests. |
| dirs | string[] | — | Additional directories to allow. |
| env | Record<string, string> | — | Extra environment variables for the CLI process. |
| systemPrompt | string | — | System prompt passed to --system-prompt. |
| debug | boolean \| 'verbose' | false | Enable debug logging. |
Turn
| Property / Method | Description |
|---|---|
| turn.on(event, fn) | Subscribe to a streaming event (see below). |
| turn.result | Promise<TurnResult> that resolves when the turn completes. |
TurnResult
| Field | Type | Description |
|---|---|---|
| text | string | Full assembled text output. |
| thinking | string | Full assembled thinking/reasoning output. |
| toolCalls | ToolCall[] | All tool calls made during the turn. |
| sessionId | string \| null | CLI session ID for resumption. |
| cost | number \| null | Cost in USD, if reported. |
| exitCode | number | CLI process exit code. |
| duration | number | Wall-clock milliseconds. |
Events
| Event | Callback Signature | Description |
|---|---|---|
| text | (chunk: string) | Incremental text output. |
| thinking | (chunk: string) | Incremental thinking/reasoning output. |
| tool_use | (tool: ToolCall) | A tool call was made (id, name, input). |
| tool_result | (result: ToolResult) | A tool returned a result (toolUseId, content). |
| permission | (request, respond) | Permission requested. Call respond('allow') or respond('deny'). |
| activity | (line: string) | Filtered stderr activity (progress stripped). |
| compaction | (summary: string) | Context window was compacted. |
| error | (err: { message, code? }) | An error occurred. |
| done | (result: TurnResult) | Turn finished. Same data as turn.result. |
What It Handles For You
- NDJSON buffering — Handles partial lines, split chunks, and malformed JSON gracefully.
- Streaming assembly — Reconstructs text, thinking, and tool-use content from
content_block_start/delta/stopsequences. - Compaction recovery — Detects mid-stream context compaction and resets streaming state without losing data.
- Flush-on-close — Processes any remaining buffered content when the CLI process exits.
- Non-streaming fallback — Handles responses delivered as complete
assistantevents instead of streaming deltas. - Session continuity — Tracks the session ID and automatically passes
--resumeon subsequentsend()calls. - Stderr filtering — Strips ANSI codes, progress bars, and spinner characters; surfaces only meaningful activity lines.
- Permission relay — Bridges CLI permission requests to your callback and writes the response back via stdin.
Requirements
- Node.js 18+
- Claude Code CLI installed and available on your PATH (or specify the path via the
clioption)
Documentation
See the docs/ directory for in-depth guides:
docs/guides/— Usage patterns and recipesdocs/reference/— Full API referencedocs/internals/— Architecture and design decisionsdocs/troubleshooting/— Common issues and solutions
License
MIT -- see LICENSE.
Credits
Born from the Synapse distributed agent swarm system.
