claude

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 19 Imported by: 0

README

claude-go-sdk

Go SDK:将 Claude Code CLI 包装为子进程后端。每轮对话启动一个 claude -p --output-format stream-json --verbose 子进程,prompt 走 stdin, stdout 的 JSONL 事件流解析为 Event 通道。零第三方依赖,仅标准库。

安装

go get github.com/justphantom/claude-go-sdk

要求:已安装并登录 Claude Code CLI(≥ 2.x),仅支持 linux/darwin。

快速开始

package main

import (
	"context"
	"fmt"

	claude "github.com/justphantom/claude-go-sdk"
)

func main() {
	c := claude.New(claude.Options{})
	ch, err := c.Run(context.Background(), claude.RunOptions{Prompt: "hello"})
	if err != nil {
		panic(err)
	}
	for ev := range ch {
		if ev.Type == claude.EventResult {
			fmt.Println(ev.Result)
		}
	}
}

调用方必须 drain 返回的 channel 直到关闭;关闭前必有终态事件 (EventResultEventError)。ctx 取消 → SIGKILL 整个进程组 → 合成 EventError

API 概览

符号 说明
Options 构造参数:CLIPath(默认 "claude")、PermissionMode(默认 "acceptEdits",CLI 的 default 模式在 -p 下会挂起交互提示)、AppendSystemPrompt、MaxConcurrent(≤0 → 4)、SettingsDir(默认 ~/.claude,支持 ~ 展开)、SettingsCacheTTL(0 → 1h;<0 → 禁用缓存)、Logger(nil → 静默)
RunOptions 单轮参数:Prompt、Directory、SessionID(非空 → --resume)、Model、PermissionMode / EffortLevel / SettingsFile(每轮覆盖,空用 Client 默认)、MaxTurns(>0 → --max-turns,失控/成本护栏)、AllowedTools / DisallowedTools(原样透传 --allowedTools / --disallowedTools)、AddDirs(每项一个 --add-dir,放开 cwd 外目录访问)、LineSink(逐行旁路原始 stream-json)
Event 扁平事件结构体,含 StopReason / DurationAPIMs(result 行元数据)与 Raw(原始行)
New(opts Options) *Client 构造客户端
(*Client) Run(ctx, RunOptions) (<-chan Event, error) 启动一轮,返回事件通道
(*Client) IsReady(ctx) error <cli> --version 探活(10s 超时)
(*Client) ListSettings(ctx) ([]string, error) 扫描 settings 目录(带 TTL 缓存)
ParseEvent(line string) ([]Event, error) 解析单行 stream-json(回放归档用)
IsStaleSession(e Event) bool 判定失效会话终态错误(result + is_error + "No conversation found"),子串匹配集中于此,CLI 改文案只修一处
常量 EventSystem/EventText/EventThinking/EventToolUse/EventToolResult/EventResult/EventError/EventTaskStarted/EventTaskProgress/EventTaskNotificationSubtypeInitPermissionModeAcceptEdits/PermissionModePlan/PermissionModeBypassPermissions

事件模型

line type 处理
system subtype init(携带 session_id/model)、task_*(子代理生命周期)
assistant/user message.content[] → text/thinking/tool_use/tool_result,一行可产多事件
result 终态:最终答案、cost、duration、turns、token 用量;坏行有宽松二次解析兜底
未知类型 原样转发(前向兼容),Raw 保留

解析失败仅记录并跳过,不致错;pump 保证关闭 channel 前必发终态事件。 LineSink 可逐行旁路原始输出(归档用)。

会话管理

懒会话:首轮 SessionID 留空,从 system/init 事件捕获 session_id 由调用方持久化,后续轮次经 RunOptions.SessionID--resume。 失效会话(IsStaleSession 判定)的重试策略由消费侧决定,SDK 不内置。

兼容政策

  • 本 SDK 遵循 semver;v1 前 minor 版本可能新增导出符号/字段,但绝不删改既有导出。
  • Schema 漂移策略:未知 line/block 类型原样转发、Raw 保留原始行、result 行有宽松二次解析兜底——CLI 升级不产生硬失败。
  • String 冻结:Event.Type/SubtypePermissionModeEffortLevel 等保持 string + 常量的设计已冻结,不会引入命名类型(下游可安全按字符串常量比较)。

平台约束

取消依赖 Setpgid + syscall.Kill(-pid)(Unix 专有),仅支持 linux/darwin,构建期强制(//go:build linux || darwin),不做 Windows 抽象。运行机器须安装 Claude Code CLI ≥ 2.x。

集成测试

单测默认全部本地可跑。真实 CLI 端到端测试用环境变量门槛:

CLAUDE_SDK_INTEGRATION=1 go test -run TestIntegration ./...

License

MIT,见 LICENSE。

Documentation

Overview

Package claude wraps the Claude Code CLI as a standalone SDK.

The SDK shells out to the `claude` binary in print/stream-json mode per turn and consumes a stream of events from stdout. A Run returns a channel of parsed Events terminated by a result or error event.

Minimal example:

c := claude.New(claude.Options{})
ch, err := c.Run(ctx, claude.RunOptions{Prompt: "hello"})
if err != nil {
	// handle
}
for ev := range ch {
	// consume ev.Type / ev.Text / ev.Result
}

Index

Constants

View Source
const (
	// EventSystem: a system line. Subtype discriminates further:
	// "init" (carries the session id), "thinking_tokens".
	EventSystem = "system"
	// EventText: an assistant text content block (a chunk of the reply).
	EventText = "text"
	// EventThinking: an assistant thinking content block (reasoning trace).
	EventThinking = "thinking"
	// EventToolUse: an assistant tool invocation (name + JSON input).
	EventToolUse = "tool_use"
	// EventToolResult: a tool_result block echoed back (output of a tool).
	EventToolResult = "tool_result"
	// EventResult: terminal line (subtype success/error) with the final
	// answer and run metadata (cost, duration). Always the last event.
	EventResult = "result"
	// EventError: synthesized by the client on subprocess failure, parse
	// error, or context cancellation. Terminal like EventResult.
	EventError = "error"

	// Subagent task lifecycle. Claude emits these as system lines with a
	// task_* subtype when a Task/Agent tool spawns a local subagent. They
	// carry the subagent type, a live description, and cumulative usage so
	// the caller can surface subagent progress instead of dropping it.
	EventTaskStarted      = "task_started"
	EventTaskProgress     = "task_progress"
	EventTaskNotification = "task_notification"
)

EventType constants for the flat Type field carried by Event. These collapse the Claude Code stream-json line "type" plus the per-block "content[].type" into a single discriminator the caller can switch on.

View Source
const (
	// PermissionModeAcceptEdits auto-accepts edits but surfaces other
	// permission-gated actions to the caller.
	PermissionModeAcceptEdits = "acceptEdits"

	// PermissionModePlan runs in read-only planning mode; no mutations.
	PermissionModePlan = "plan"

	// PermissionModeBypassPermissions skips all permission checks. Most
	// permissive; use only when the working directory is disposable.
	PermissionModeBypassPermissions = "bypassPermissions"
)

PermissionMode controls how the Claude Code CLI handles tool permission requests during a run. In -p (print / stream) mode the CLI is non-interactive, so a mode that never blocks on user input is required.

These map 1:1 to the Claude Code CLI's --permission-mode flag values.

View Source
const (
	SubtypeInit = "init"
)

System subtypes, exposed as constants so callers can switch on Subtype without sprinkling string literals through the consumer code.

Variables

This section is empty.

Functions

func IsStaleSession

func IsStaleSession(e Event) bool

IsStaleSession reports whether e is the CLI's "session no longer exists" terminal error (result line, is_error, "No conversation found with session ID: …"). Centralised here so consumers don't hand-roll string matching — the CLI may reword the message, and only this substring is checked so a fix applies in one place.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps the Claude Code CLI. It is safe for concurrent use: each Run spawns one subprocess, and a semaphore caps the number of parallel subprocesses at MaxConcurrent.

func New

func New(opts Options) *Client

New builds a Client from opts, applying the documented defaults for any zero-valued fields (see Options).

func (*Client) IsReady

func (c *Client) IsReady(ctx context.Context) error

IsReady verifies the CLI is installed and invocable by running `<cliPath> --version`. Returns an error suitable for a startup health gate.

func (*Client) ListSettings

func (c *Client) ListSettings(_ context.Context) ([]string, error)

ListSettings returns the absolute paths of settings files in the settings directory, sorted by filename. Results are cached for c.settingsTTL; a cache miss rescans the directory. When settingsTTL <= 0 caching is disabled and every call rescans. An empty settingsDir (HOME unset and Options left empty) yields an error so the caller can surface it instead of showing an empty list.

func (*Client) Run

func (c *Client) Run(ctx context.Context, opts RunOptions) (<-chan Event, error)

Run starts one Claude Code CLI subprocess for opts and returns a channel of parsed Events. The channel is always closed by the client after the subprocess exits; a terminal Event (EventResult on success, EventError on failure/cancellation) is emitted immediately before close when the CLI itself did not emit one.

The caller MUST drain the channel until it is closed. Run blocks acquiring a concurrency slot until ctx is cancelled (returning ctx.Err()) or a slot frees up.

type Event

type Event struct {
	Type      string // one of the Event* constants
	Subtype   string
	SessionID string
	Model     string
	Text      string

	ToolID    string
	ToolName  string
	ToolInput string

	// Subagent task fields, populated only on EventTask* events. TaskID is
	// the stable identifier correlating started/progress/notification of the
	// same subagent (unlike TaskType/TaskDesc which drift across the lifecycle);
	// TaskType is the subagent type (e.g. "Explore"); TaskKind is the task class
	// from upstream ("local_agent" for true subagents, "local_bash" for shell
	// subprocesses); TaskDesc is the live description that changes per progress
	// tick; TaskTokens/TaskSteps/TaskMs are the cumulative usage reported by Claude.
	TaskID     string
	TaskType   string
	TaskKind   string
	TaskDesc   string
	TaskTokens int
	TaskSteps  int
	TaskMs     int64

	IsToolError bool

	Result     string
	CostUSD    float64
	DurationMs int64
	IsError    bool
	NumTurns   int

	// StopReason is the model's stop_reason from the result line
	// (e.g. "end_turn"); DurationAPIMs is the API-only wall time
	// (duration_api_ms), complementing DurationMs which includes CLI
	// overhead.
	StopReason    string
	DurationAPIMs int64

	// Token counts from a result line. InputTokens/OutputTokens are the
	// non-cache breakdown; CacheRead/CacheCreation carry the prompt-cache
	// hits and writes so callers can record the full per-session picture.
	InputTokens   int
	OutputTokens  int
	CacheRead     int
	CacheCreation int

	// Raw is retained for debug logging and parsing sub-fields (e.g.
	// subagent events) by the caller.
	Raw string
}

Event is a parsed Claude Code stream-json event, flattened for easy consumption. One input line may yield several Events (an assistant message can carry multiple content blocks); a terminal Event (EventResult or EventError) is always emitted last.

func ParseEvent

func ParseEvent(line string) ([]Event, error)

ParseEvent decodes one stream-json line into zero or more Events. Exported so callers can replay captured raw lines (e.g. from an archive) through the same parser the client uses.

type Options

type Options struct {
	// CLIPath is the claude binary to invoke. Empty defaults to "claude"
	// (PATH lookup).
	CLIPath string
	// PermissionMode is the default --permission-mode. Empty defaults to
	// "acceptEdits": the CLI's own "default" mode prompts interactively,
	// which hangs forever under -p (non-interactive) mode.
	PermissionMode string
	// AppendSystemPrompt is passed verbatim as --append-system-prompt.
	AppendSystemPrompt string
	// MaxConcurrent caps parallel subprocesses. <=0 defaults to 4.
	MaxConcurrent int
	// SettingsDir is scanned by ListSettings. Empty defaults to ~/.claude;
	// a leading "~" is expanded to $HOME.
	SettingsDir string
	// SettingsCacheTTL bounds the ListSettings cache. 0 defaults to 1h;
	// <0 disables caching (every call rescans).
	SettingsCacheTTL time.Duration
	// Logger receives debug/warn lines. nil defaults to a discard logger.
	Logger *slog.Logger
}

Options configures a Client at construction time.

type RunOptions

type RunOptions struct {
	// Prompt is sent to the CLI via stdin.
	Prompt string
	// Directory sets the subprocess working directory (cmd.Dir).
	Directory string
	// SessionID, when non-empty, is passed as --resume to continue an
	// existing Claude session. Empty starts a fresh session; the
	// session_id returned in the system/init event should be persisted
	// by the caller for subsequent turns.
	SessionID string
	// Model optionally sets the model for this turn (--model).
	Model string
	// PermissionMode optionally overrides the Client's configured
	// --permission-mode for this turn. Empty falls back to the Client's
	// permission mode.
	PermissionMode string
	// EffortLevel optionally sets the Claude --effort level for this
	// turn. Empty falls back to Claude's default effort behavior.
	EffortLevel string
	// MaxTurns, when >0, is passed as --max-turns: the CLI aborts the
	// turn after N agent steps. Runaway/cost guard — without it a
	// misbehaving agent can loop tool calls indefinitely.
	MaxTurns int
	// AllowedTools, when non-empty, is passed verbatim as
	// --allowedTools (the CLI's own list syntax, e.g. "Bash,Read").
	AllowedTools string
	// DisallowedTools, when non-empty, is passed verbatim as
	// --disallowedTools (same list syntax).
	DisallowedTools string
	// AddDirs appends one --add-dir per entry, granting the CLI access
	// to directories outside the working directory (the CLI sandboxes
	// tool file access to cwd by default, blocking outside paths).
	AddDirs []string
	// SettingsFile optionally sets the Claude --settings file path for
	// this turn. Empty means "not set". The caller is responsible for any
	// env-var expansion before passing the path here; the client appends
	// it verbatim to the CLI args.
	SettingsFile string
	// LineSink, when non-nil, receives every raw stream-json line verbatim
	// (line + "\n") as read from stdout, before parsing. Used to archive
	// the complete CLI return stream. Writes are best-effort: errors are
	// ignored so an archive failure can never fail the run.
	LineSink io.Writer
}

RunOptions describes a single agent turn.

Directories

Path Synopsis
examples
basic command
Command basic runs a single Claude turn and prints the event stream.
Command basic runs a single Claude turn and prints the event stream.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL