ClaudeCode.Net
0.2.5
dotnet add package ClaudeCode.Net --version 0.2.5
NuGet\Install-Package ClaudeCode.Net -Version 0.2.5
<PackageReference Include="ClaudeCode.Net" Version="0.2.5" />
<PackageVersion Include="ClaudeCode.Net" Version="0.2.5" />
<PackageReference Include="ClaudeCode.Net" />
paket add ClaudeCode.Net --version 0.2.5
#r "nuget: ClaudeCode.Net, 0.2.5"
#:package ClaudeCode.Net@0.2.5
#addin nuget:?package=ClaudeCode.Net&version=0.2.5
#tool nuget:?package=ClaudeCode.Net&version=0.2.5
ClaudeCode.NET
Drive Claude Code sessions from .NET.
The Claude Agent SDK ships for TypeScript and Python. .NET developers who want to orchestrate Claude Code sessions end up hand-rolling the same plumbing every time: find the CLI, spawn the process, escape the arguments, pump stream-json output, parse events, capture the session ID for resume. This library is that plumbing, done once and tested.
- Typed event stream —
IAsyncEnumerable<ClaudeStreamEvent>over the CLI's stream-json output: assistant text, tool calls, tool results, session stats, rate-limit signals - Safe argument handling — prompts pass through
ProcessStartInfo.ArgumentList, so newlines, quotes, and backslashes arrive verbatim (no shell-escaping bugs) - Session chaining — capture
SessionIdfrom one run, pass it asResumeSessionIdto the next - Clean cancellation — cancelling the token kills the entire CLI process tree; no orphaned
claudeprocesses - Max plan friendly — uses the CLI's
claude loginauth; no API key required - No logging opinion — takes an optional
Microsoft.Extensions.Logging.ILogger; silent by default
Parsing is verified against real CLI output, not guessed from docs.
Requirements
.NET 8.0 or later
Install the Claude Code CLI (native installer, auto-updates):
# Windows (PowerShell) irm https://claude.ai/install.ps1 | iex# macOS / Linux / WSL curl -fsSL https://claude.ai/install.sh | bashThen authenticate: run
claudeand follow the login prompts.
Authentication & Anthropic's terms
ClaudeCode.Net never handles credentials. The library contains no authentication
code and makes no API calls — you can audit this in about a minute: there is no
HttpClient, no token handling, and no reference to Anthropic's API anywhere in the
source. Its entire integration surface is spawning the official Claude Code CLI
as a child process and parsing its output stream.
That means authentication belongs to the CLI, exactly as if you ran it yourself in a terminal, and you have two options:
- Claude subscription (Pro/Max): run
claudeonce and log in. Sessions started by this library then run under your subscription, within its rate limits. - API key (metered billing): set
ANTHROPIC_API_KEYin the environment of the process you spawn. Identical library code; pay-per-token billing.
Why this architecture matters for compliance: Anthropic's terms prohibit taking OAuth tokens from consumer accounts into other products — the prohibited pattern is extracting your credential and calling the API from a different client. This library does the opposite: every request originates from Anthropic's own client, authenticated by its own login flow. This is the same architecture as Anthropic's official Claude Agent SDK, which likewise wraps the CLI.
A caveat about the future: Anthropic has signaled (announced May 2026, then postponed) that programmatic/SDK-style usage on subscriptions may eventually be billed from a separate credit pool than interactive use. If that lands, subscription users of this library may need to budget differently or switch to an API key — a billing change, not a compatibility one; the code path is identical either way. Check Anthropic's consumer terms and the Claude Code docs for current policy; this README describes the situation as of July 2026 and is not legal advice.
Quick start
using ClaudeCode;
var session = new ClaudeSession();
var options = new ClaudeSessionOptions
{
WorkingDirectory = "/path/to/repo",
Model = "claude-sonnet-5", // optional; omit for the CLI default
MaxTurns = 50
};
// Streaming: react to events as they arrive
await foreach (var evt in session.StreamAsync("Fix the failing test in FooTests", options))
{
switch (evt.Kind)
{
case ClaudeStreamEventKind.AssistantText: Console.WriteLine(evt.Text); break;
case ClaudeStreamEventKind.ToolUse: Console.WriteLine($"-> {evt.ToolName}"); break;
case ClaudeStreamEventKind.Result: Console.WriteLine($"done in {evt.NumTurns} turns"); break;
}
}
// Or aggregate: one call, one result
var result = await session.RunAsync("Summarize this repo's architecture", options);
Console.WriteLine(result.AssistantText);
Console.WriteLine($"session {result.SessionId}, {result.NumTurns} turns");
Session chaining
Each --print invocation is a fresh session, but you can resume the previous one so Claude keeps its context:
var first = await session.RunAsync("Implement feature A", options);
var chained = options with { ResumeSessionId = first.SessionId };
var second = await session.RunAsync("Now implement feature B", chained);
Rate limits
ClaudeRunResult.RateLimitDetected is true when the CLI reports a rate limit (via its
rate_limit_event stream event or recognizable error text) — poll-and-retry policies
are yours to choose; the library just gives you a reliable signal.
Unattended tool use
SkipPermissions defaults to true (--dangerously-skip-permissions), because non-interactive
sessions can't answer permission prompts — without it, any Bash/Write tool call fails.
Only run unattended sessions in a workspace you trust Claude to modify.
Long prompts (stdin delivery)
Windows caps a process command line at ~32k characters, so a large prompt — a replayed
conversation, an embedded document — fails to launch when passed as an argument
("The filename or extension is too long"). Since 0.2.5, prompts longer than
ClaudeSessionOptions.StdinAutoThreshold (8k chars) are piped to the CLI's stdin
automatically. PromptDelivery on the options overrides the behavior: Auto (default),
Stdin (always pipe), or Argument (pre-0.2.5 behavior).
Cookbook
Commit messages from your staged diff (pure text generation - one turn, no tools):
var diff = /* output of: git diff --cached */;
var result = await session.RunAsync(
$"Write a conventional commit message for this diff. Reply with ONLY the message.\n\n{diff}",
new ClaudeSessionOptions { WorkingDirectory = repoDir, MaxTurns = 1, SkipPermissions = false });
Console.WriteLine(result.ResultText);
Live progress display - render what the agent is doing as it works:
await foreach (var evt in session.StreamAsync(prompt, options))
{
var line = evt.Kind switch
{
ClaudeStreamEventKind.ToolUse => $" [tool] {evt.ToolName}",
ClaudeStreamEventKind.AssistantText => $" {evt.Text}",
ClaudeStreamEventKind.Result => $" [done: {evt.NumTurns} turns]",
_ => null
};
if (line is not null) Console.WriteLine(line);
}
Unattended runs: check the result honestly. A session can "complete" while having failed - always look at the whole outcome, not just the text:
var result = await session.RunAsync(prompt, options);
if (result.RateLimitDetected && result.IsError)
/* back off and retry later */ ;
else if (result.IsError || result.ExitCode != 0)
logger.LogError("Session failed: {Text}", result.ResultText);
else
/* trust - then verify: run your tests before accepting the changes */ ;
Timeouts - cancellation kills the entire CLI process tree, no orphans:
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(20));
var result = await session.RunAsync(prompt, options, cts.Token);
Samples
Runnable projects in samples/:
CommitMessageBot- staged diff in, conventional commit message out (~40 lines)ChainedRefactor- two sessions where the second resumes the first and provably builds on its contextHelloAgent- minimal event-stream walkthrough
Status
Early (0.1.0). Extracted from SharpCoder (a private project — an AI application generator), where this code drives multi-hour autonomous build sessions. API may move before 1.0.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.