Documentation
¶
Overview ¶
Package cli provides adapters for AI-powered CLI tools.
This package implements a unified interface for interacting with various AI CLI tools such as Claude Code, Gemini CLI, and others. Each adapter handles:
- Session management via tmux
- Input delivery to the CLI
- Response extraction from history files or hooks
- Interactive state detection
Supported CLIs ¶
- Claude Code (claude): Anthropic's AI programming assistant
- Gemini (gemini): Google's AI assistant
- OpenCode (opencode): AI programming assistant
Architecture ¶
The CLI adapter pattern separates transport layer (HTTP, file I/O, tmux) from protocol logic. Each adapter:
- Creates/manages tmux sessions for the CLI
- Sends user input via tmux send-keys
- Receives responses via two mechanisms: - Hook mode: Real-time notifications when CLI completes a task (use_hook: true) - Polling mode: Periodic tmux capture when output becomes stable (use_hook: false)
- Detects interactive states (prompts, confirmations)
Thread Safety ¶
CLI adapters are not thread-safe and should not be accessed concurrently. The engine ensures serialized access to each adapter.
Index ¶
- func ExtractLastAssistantResponse(transcriptPath string) (string, error)
- func ExtractLatestInteraction(transcriptPath string) (string, string, error)
- type ACPAdapter
- func (a *ACPAdapter) Close() error
- func (a *ACPAdapter) CreateSession(sessionName string, opts ...SessionOption) error
- func (a *ACPAdapter) DeleteSession(sessionName string) error
- func (a *ACPAdapter) GetPollInterval() time.Duration
- func (a *ACPAdapter) GetPollTimeout() time.Duration
- func (a *ACPAdapter) GetStableCount() int
- func (a *ACPAdapter) HandleHookData(data []byte) (string, string, string, error)
- func (a *ACPAdapter) IsSessionAlive(sessionName string) bool
- func (a *ACPAdapter) SendInput(sessionName, input string) error
- func (a *ACPAdapter) SetEngine(engine Engine)
- func (a *ACPAdapter) StopSession(sessionName string) error
- func (a *ACPAdapter) UseHook() bool
- type ACPAdapterConfig
- type ACPTransportType
- type BaseAdapter
- func (b *BaseAdapter) CreateSession(sessionName string, opts ...SessionOption) error
- func (b *BaseAdapter) IsSessionAlive(sessionName string) bool
- func (b *BaseAdapter) SendInput(sessionName, input string) error
- func (b *BaseAdapter) Start(sessionName, startCmd string) error
- func (b *BaseAdapter) StopSession(sessionName string) error
- type CLIAdapter
- type CLIEvent
- type CLIEventType
- type ClaudeAdapter
- type ClaudeAdapterConfig
- type ContentBlock
- type Engine
- type GeminiAdapter
- type GeminiAdapterConfig
- type MessageContent
- type OpenCodeAdapter
- type OpenCodeAdapterConfig
- type OpenCodeSessionInfo
- type SessionOption
- type SessionOptions
- type StreamingCLI
- type TranscriptMessage
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractLastAssistantResponse ¶
ExtractLastAssistantResponse extracts all assistant messages after the last user message
Types ¶
type ACPAdapter ¶
type ACPAdapter struct {
// contains filtered or unexported fields
}
ACPAdapter implements CLIAdapter using Agent Client Protocol
func NewACPAdapter ¶
func NewACPAdapter(config ACPAdapterConfig) (*ACPAdapter, error)
NewACPAdapter creates a new ACP adapter
func (*ACPAdapter) CreateSession ¶
func (a *ACPAdapter) CreateSession(sessionName string, opts ...SessionOption) error
CreateSession creates a new ACP session and starts connection
func (*ACPAdapter) DeleteSession ¶
func (a *ACPAdapter) DeleteSession(sessionName string) error
DeleteSession terminates an ACP session
func (*ACPAdapter) GetPollInterval ¶
func (a *ACPAdapter) GetPollInterval() time.Duration
GetPollInterval returns polling interval (ACP uses request/response)
func (*ACPAdapter) GetPollTimeout ¶
func (a *ACPAdapter) GetPollTimeout() time.Duration
GetPollTimeout returns request timeout (for compatibility with polling mode)
func (*ACPAdapter) GetStableCount ¶
func (a *ACPAdapter) GetStableCount() int
GetStableCount returns stable count (not used in ACP mode)
func (*ACPAdapter) HandleHookData ¶
HandleHookData - not used in ACP mode
func (*ACPAdapter) IsSessionAlive ¶
func (a *ACPAdapter) IsSessionAlive(sessionName string) bool
IsSessionAlive checks if session is active
func (*ACPAdapter) SendInput ¶
func (a *ACPAdapter) SendInput(sessionName, input string) error
SendInput sends input to the ACP server
func (*ACPAdapter) SetEngine ¶
func (a *ACPAdapter) SetEngine(engine Engine)
SetEngine sets the engine reference for sending responses
func (*ACPAdapter) StopSession ¶ added in v0.1.5
func (a *ACPAdapter) StopSession(sessionName string) error
StopSession delegates to DeleteSession for ACP sessions.
func (*ACPAdapter) UseHook ¶
func (a *ACPAdapter) UseHook() bool
UseHook returns false - ACP doesn't use hook mode
type ACPAdapterConfig ¶
type ACPAdapterConfig struct {
// Idle timeout - max time without any activity before cancelling request
// Default: 5 minutes. If the agent is actively working (sending updates),
// the request will continue. Only cancelled if there's no activity for this duration.
IdleTimeout time.Duration `yaml:"idle_timeout"`
// Max total timeout - absolute maximum time for a request regardless of activity
// Default: 1 hour. This is a hard limit to prevent truly hung requests.
MaxTotalTimeout time.Duration `yaml:"max_total_timeout"`
// Environment variables for ACP server process
Env map[string]string `yaml:"env"`
// Deprecated: Use IdleTimeout instead
// Kept for backward compatibility
RequestTimeout time.Duration `yaml:"request_timeout"`
}
ACPAdapterConfig configuration for ACP adapter
type ACPTransportType ¶
type ACPTransportType string
ACPTransportType represents the ACP transport type
const ( ACPTransportStdio ACPTransportType = "stdio" ACPTransportTCP ACPTransportType = "tcp" ACPTransportUnix ACPTransportType = "unix" )
type BaseAdapter ¶
type BaseAdapter struct {
// contains filtered or unexported fields
}
BaseAdapter provides common fields and methods for CLI adapters
func NewBaseAdapter ¶
func NewBaseAdapter(name, startCmd string, delay int) BaseAdapter
NewBaseAdapter creates a new BaseAdapter
func (*BaseAdapter) CreateSession ¶
func (b *BaseAdapter) CreateSession(sessionName string, opts ...SessionOption) error
CreateSession creates a new tmux session and starts the CLI This method is idempotent: if the session already exists, it returns successfully The transportURL parameter is ignored by base adapters (only used by ACP)
func (*BaseAdapter) IsSessionAlive ¶
func (b *BaseAdapter) IsSessionAlive(sessionName string) bool
func (*BaseAdapter) SendInput ¶
func (b *BaseAdapter) SendInput(sessionName, input string) error
SendInput sends input to the CLI via tmux
func (*BaseAdapter) Start ¶
func (b *BaseAdapter) Start(sessionName, startCmd string) error
Start starts the CLI in the specified tmux session
func (*BaseAdapter) StopSession ¶ added in v0.1.5
func (b *BaseAdapter) StopSession(sessionName string) error
StopSession kills the tmux session.
type CLIAdapter ¶
type CLIAdapter interface {
// SendInput sends input to the CLI (via tmux send-keys)
SendInput(sessionName, input string) error
// HandleHookData handles raw hook data from the CLI
// The adapter is responsible for:
// - Parsing the data (in any format: JSON, text, etc.)
// - Extracting the last user prompt for tmux filtering
// - Extracting the session name from the data
// - Processing the hook data and generating the response
//
// This interface is protocol-agnostic - it works with HTTP, gRPC, message queues, etc.
// The engine is responsible for extracting the raw data from the transport layer.
//
// Parameter data: raw hook data (bytes)
// Returns: (sessionName, lastUserPrompt, responseText, error)
// - sessionName: which session this hook is for (cwd)
// - lastUserPrompt: the last user's input (for filtering tmux output)
// - responseText: the CLI's response to send back to the user
// - error: any error that occurred
HandleHookData(data []byte) (sessionName string, lastUserPrompt string, response string, err error)
// IsSessionAlive checks if the session is still alive
IsSessionAlive(sessionName string) bool
// CreateSession creates a new session and starts the CLI.
// Use WithWorkDir, WithStartCmd, WithTransportURL, WithEnv, WithYolo, WithResume options.
CreateSession(sessionName string, opts ...SessionOption) error
// StopSession stops and cleans up a running session.
StopSession(sessionName string) error
}
CLIAdapter defines the interface for CLI adapters
type CLIEvent ¶ added in v0.1.8
type CLIEvent struct {
Type CLIEventType
Content string // Text content or tool output
ToolID string // For tool_use/tool_result: tool call ID
ToolName string // For tool_use/tool_result: tool name
ToolMeta map[string]string // For tool_use: command, file_path, etc.
}
CLIEvent represents an intermediate event during CLI processing.
type CLIEventType ¶ added in v0.1.8
type CLIEventType string
CLIEventType enumerates the kinds of events emitted during streaming.
const ( CLIEventText CLIEventType = "text" // Text output chunk CLIEventToolUse CLIEventType = "tool_use" // Tool invocation started CLIEventToolResult CLIEventType = "tool_result" // Tool invocation completed CLIEventThinking CLIEventType = "thinking" // Thinking/reasoning output CLIEventPermission CLIEventType = "permission" // Permission request pending user action CLIEventDone CLIEventType = "done" // Turn completed CLIEventUsage CLIEventType = "usage" // Token usage information )
type ClaudeAdapter ¶
type ClaudeAdapter struct {
BaseAdapter
}
ClaudeAdapter implements CLIAdapter for Claude Code
func NewClaudeAdapter ¶
func NewClaudeAdapter(config ClaudeAdapterConfig) (*ClaudeAdapter, error)
NewClaudeAdapter creates a new Claude Code adapter
func (*ClaudeAdapter) HandleHookData ¶
HandleHookData handles raw hook data from Claude Code Expected data format (JSON):
{"cwd": "/path/to/workdir", "session_id": "...", "transcript_path": "...", ...}
This returns the cwd as the session identifier, which will be matched against the configured session's work_dir in the engine.
Parameter data: raw hook data (JSON bytes) Returns: (cwd, lastUserPrompt, response, error)
type ClaudeAdapterConfig ¶
type ClaudeAdapterConfig struct {
Env map[string]string // Environment variables to set for the CLI process
}
ClaudeAdapterConfig configuration for Claude Code adapter
type ContentBlock ¶
type ContentBlock struct {
Type string `json:"type"` // "text", "thinking", "image", etc.
Text string `json:"text,omitempty"`
Thinking string `json:"thinking,omitempty"`
}
ContentBlock represents a block of content (text, thinking, image, etc.)
type Engine ¶
type Engine interface {
SendToBot(platform, channel, message string)
SendResponseToSession(sessionName, message string)
}
Engine defines the interface for sending responses to users. It's implemented by the core Engine and passed to adapters.
type GeminiAdapter ¶
type GeminiAdapter struct {
BaseAdapter
}
GeminiAdapter implements CLIAdapter for Gemini CLI
func NewGeminiAdapter ¶
func NewGeminiAdapter(config GeminiAdapterConfig) (*GeminiAdapter, error)
NewGeminiAdapter creates a new Gemini CLI adapter
func (*GeminiAdapter) ExtractLatestInteraction ¶
func (g *GeminiAdapter) ExtractLatestInteraction(transcriptPath string, cwd string) (string, string, error)
ExtractLatestInteraction exports the latest Gemini response extraction logic
func (*GeminiAdapter) HandleHookData ¶
HandleHookData handles raw hook data from Gemini CLI Expected data format (JSON):
{"session_id": "...", "cwd": "...", ...}
Gemini stores history in: ~/.gemini/tmp/{project_hash}/chats/session-*.json where project_hash = SHA256(project_path)
This returns the cwd as the session identifier, which will be matched against the configured session's work_dir in the engine.
Parameter data: raw hook data (JSON bytes) Returns: (cwd, lastUserPrompt, response, error)
type GeminiAdapterConfig ¶
type GeminiAdapterConfig struct {
Env map[string]string // Environment variables to set for the CLI process
}
GeminiAdapterConfig configuration for Gemini CLI adapter
type MessageContent ¶
type MessageContent struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"` // "message" for assistant
Role string `json:"role,omitempty"` // "user" or "assistant"
Model string `json:"model,omitempty"` // Model name
Content []ContentBlock `json:"content,omitempty"`
ContentText string `json:"-"` // Extracted when content is a string
StopReason string `json:"stop_reason,omitempty"` // null if incomplete, "end_turn"/"max_tokens" if complete
}
MessageContent represents the message content structure Note: content can be either a string (user messages) or an array (assistant messages)
func (*MessageContent) UnmarshalJSON ¶
func (mc *MessageContent) UnmarshalJSON(data []byte) error
UnmarshalJSON implements custom JSON unmarshaling for MessageContent
type OpenCodeAdapter ¶
type OpenCodeAdapter struct {
BaseAdapter
}
OpenCodeAdapter implements CLIAdapter for OpenCode
func NewOpenCodeAdapter ¶
func NewOpenCodeAdapter(config OpenCodeAdapterConfig) (*OpenCodeAdapter, error)
NewOpenCodeAdapter creates a new OpenCode adapter
func (*OpenCodeAdapter) ExtractLatestInteraction ¶
func (o *OpenCodeAdapter) ExtractLatestInteraction(transcriptPath string) (string, string, error)
ExtractLatestInteraction legacy support
func (*OpenCodeAdapter) HandleHookData ¶
HandleHookData handles raw hook data from OpenCode Expected data format (JSON):
{"cwd": "/path/to/workdir", "session_id": "...", "hook_event_name": "..."}
Returns: (cwd, lastUserPrompt, response, error)
type OpenCodeAdapterConfig ¶
type OpenCodeAdapterConfig struct {
Env map[string]string // Environment variables to set for the CLI process
}
OpenCodeAdapterConfig configuration for OpenCode adapter
type OpenCodeSessionInfo ¶
type OpenCodeSessionInfo struct {
ID string `json:"id"`
ProjectID string `json:"projectID"`
Time struct {
Updated int64 `json:"updated"`
} `json:"time"`
}
OpenCodeSessionInfo represents the structure of an OpenCode session info file
type SessionOption ¶ added in v0.1.5
type SessionOption func(*SessionOptions)
SessionOption configures a SessionOptions value.
func WithEnv ¶ added in v0.1.5
func WithEnv(env map[string]string) SessionOption
WithEnv sets session-level environment variables.
func WithResume ¶ added in v0.1.5
func WithResume(resume bool) SessionOption
WithResume marks the session as having prior history so the first turn uses resume mode.
func WithStartCmd ¶ added in v0.1.5
func WithStartCmd(cmd string) SessionOption
WithStartCmd sets the command to start the CLI.
func WithTransportURL ¶ added in v0.1.5
func WithTransportURL(transportURL string) SessionOption
WithTransportURL sets the transport URL (for ACP adapter).
func WithWorkDir ¶ added in v0.1.5
func WithWorkDir(dir string) SessionOption
WithWorkDir sets the working directory for the session.
func WithYolo ¶ added in v0.1.5
func WithYolo(yolo bool) SessionOption
WithYolo enables auto-approve mode (each CLI appends its own flag).
type SessionOptions ¶ added in v0.1.5
type SessionOptions struct {
WorkDir string
StartCmd string
TransportURL string
Env map[string]string
Yolo bool
Resume bool // Resume previous conversation on first turn
}
SessionOptions holds parameters for session creation.
func ApplySessionOptions ¶ added in v0.1.5
func ApplySessionOptions(opts []SessionOption) SessionOptions
ApplySessionOptions applies all options to a new SessionOptions value.
type StreamingCLI ¶ added in v0.1.8
type StreamingCLI interface {
// SendInputStreaming sends input and returns a channel of intermediate events.
// The channel is closed when the turn completes (normally or on error).
// Implementations must also satisfy CLIAdapter for session management.
SendInputStreaming(sessionName, input string) (<-chan CLIEvent, error)
}
StreamingCLI is an optional interface for CLI adapters that support receiving intermediate events during processing. The engine detects this via type assertion and uses it for rich streaming replies.
type TranscriptMessage ¶
type TranscriptMessage struct {
Type string `json:"type"` // "user", "assistant", "progress", etc.
SessionID string `json:"sessionId"`
IsMeta bool `json:"isMeta"` // Legacy field, replaced by IsSidechain in newer versions
IsSidechain bool `json:"isSidechain"` // Replaces isMeta in Claude Code >= 2.1.x
IsCompactSummary bool `json:"isCompactSummary"` // Context compaction summary messages
Message MessageContent `json:"message"`
}
TranscriptMessage represents a single message in Claude Code's transcript.jsonl Each line in the file is a JSON object with this structure