cc-local-use
v0.5.1
Published
A TypeScript library for calling local Claude Code CLI with sane defaults
Readme
cc-local-use
A TypeScript library for calling the local Claude Code CLI or Gemini CLI programmatically, with sane defaults.
Requirements
- Node.js >= 18
- Claude Code CLI installed and authenticated (
claudecommand available in PATH) - Or Gemini CLI installed and authenticated (
geminicommand available in PATH)
Installation
npm install cc-local-useUsage
Basic query
import { createClient } from "cc-local-use";
const cc = createClient({ model: "claude-opus-4-6" });
const answer = await cc.query("Summarise this file", { cwd: "/your/project" });
console.log(answer);Streaming
import { createClient } from "cc-local-use";
const cc = createClient();
for await (const chunk of cc.stream("Write a short poem about the sea")) {
process.stdout.write(chunk);
}JSON mode (with metadata)
import { createClient } from "cc-local-use";
const cc = createClient();
const result = await cc.queryJSON("What is 2 + 2? Reply with just the number.");
console.log(result.result); // "4"
console.log(result.cost_usd); // e.g. 0.000123
console.log(result.session_id); // session ID for resumingResume a session
const first = await cc.queryJSON("My name is Alice.");
const reply = await cc.query("What is my name?", {
resumeSession: first.session_id,
});
console.log(reply); // "Your name is Alice."Using Gemini CLI
import { createClient } from "cc-local-use";
const gemini = createClient({
provider: "gemini",
model: "gemini-2.5-pro",
});
const answer = await gemini.query("What is the capital of France?");
console.log(answer);The same query, stream, queryJSON, and checkPromptInjection methods work with both providers. Gemini results are normalized to the same ResultEvent shape (cost_usd and num_turns will be 0).
API
createClient(options?)
Creates a new client.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| provider | "claude" \| "gemini" | "claude" | Which CLI to use |
| cliPath | string | auto | Path to the CLI executable (defaults to "claude" or "gemini") |
| claudePath | string | "claude" | Deprecated. Use cliPath instead |
| model | string | — | Default model for all calls |
| cwd | string | process.cwd() | Default working directory for the subprocess |
| dangerouslySkipPermissions | boolean | true | --dangerously-skip-permissions (Claude) / --yolo (Gemini) |
| promptInjectionGuard | boolean | false | Check prompts for injection attempts before executing |
Returns a ClaudeCodeClient with four methods:
cc.query(prompt, options?)
Sends a prompt and returns the full text response as a Promise<string>.
cc.stream(prompt, options?)
Sends a prompt and returns an AsyncGenerator<string, ResultEvent> that yields text chunks as they arrive. The generator's return value is the final ResultEvent.
cc.queryJSON(prompt, options?)
Sends a prompt and returns a Promise<ResultEvent> with the structured result including metadata.
QueryOptions
All three methods accept an optional QueryOptions object:
| Option | Type | Description |
|--------|------|-------------|
| model | string | Override the model for this call |
| systemPrompt | string | System prompt prepended to the conversation |
| maxTurns | number | Max number of agentic turns (--max-turns). Claude only |
| resumeSession | string | Resume an existing session by ID (--resume) |
| cwd | string | Working directory for the subprocess |
| extraArgs | string[] | Extra raw CLI arguments appended verbatim |
| promptInjectionGuard | boolean | Override client-level injection guard setting |
ResultEvent
interface ResultEvent {
type: "result";
subtype: "success" | "error";
is_error: boolean;
result: string;
cost_usd: number;
duration_ms: number;
duration_api_ms: number;
session_id: string;
num_turns: number;
}Error handling
On non-zero exit, all methods throw a ClaudeCodeError:
type ClaudeCodeError = Error & {
readonly exitCode: number | null;
readonly stderr: string;
};import { createClient } from "cc-local-use";
const cc = createClient();
try {
await cc.query("hello");
} catch (err) {
if (err.name === "ClaudeCodeError") {
console.error("Exit code:", err.exitCode);
console.error("Stderr:", err.stderr);
}
}Docker で使用する場合
Docker コンテナ内で Claude Code CLI を実行する場合、root ユーザーのままだとコマンドが正常に動作しません。これは Claude Code がセキュリティ上の理由から root での実行を制限しているためです。
非 root ユーザーを作成する(推奨)
Dockerfile で専用ユーザーを作成して切り替えます。
FROM node:20-slim
# Claude Code CLI のインストール
RUN npm install -g @anthropic-ai/claude-code
# 非 root ユーザーを作成
RUN groupadd -r appuser && useradd -r -g appuser -m appuser
WORKDIR /app
COPY . .
RUN npm install
# ユーザーを切り替え
USER appuser
CMD ["node", "index.js"]環境変数で root 実行を許可する
非 root ユーザーへの切り替えが難しい場合は、環境変数 CLAUDE_CODE_SKIP_ROOT_CHECK=1 を設定することで root での実行を許可できます。
FROM node:20-slim
RUN npm install -g @anthropic-ai/claude-code
WORKDIR /app
COPY . .
RUN npm install
ENV CLAUDE_CODE_SKIP_ROOT_CHECK=1
CMD ["node", "index.js"]または docker run 時に指定します。
docker run -e CLAUDE_CODE_SKIP_ROOT_CHECK=1 your-image注意: セキュリティの観点から、可能な限り非 root ユーザーでの実行を推奨します。
Provider differences
| Feature | Claude | Gemini |
|---------|--------|--------|
| Non-interactive mode | --print | -p |
| Permission skip | --dangerously-skip-permissions | --yolo |
| System prompt | --system-prompt flag | Temp file via GEMINI_SYSTEM_MD env var |
| Max turns | --max-turns flag | Not supported via CLI |
| cost_usd | Reported | Always 0 |
| num_turns | Reported | Always 0 |
Notes
- All calls automatically pass the non-interactive flag and permission-skip flag to the CLI.
- The CLI process inherits the current process's environment variables (
process.env).
