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, workDir, startCmd, transportURL string, env map[string]string) 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) UseHook() bool
- type ACPAdapterConfig
- type ACPTransportType
- type BaseAdapter
- func (b *BaseAdapter) CreateSession(sessionName, workDir, startCmd, transportURL string, env map[string]string) error
- func (b *BaseAdapter) IsSessionAlive(sessionName string) bool
- func (b *BaseAdapter) SendInput(sessionName, input string) error
- func (b *BaseAdapter) Start(sessionName, startCmd string) error
- type CLIAdapter
- type ClaudeAdapter
- type ClaudeAdapterConfig
- type ContentBlock
- type Engine
- type GeminiAdapter
- type GeminiAdapterConfig
- type MessageContent
- type OpenCodeAdapter
- type OpenCodeAdapterConfig
- type OpenCodeSessionInfo
- 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, workDir, startCmd, transportURL string, env map[string]string) 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) 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, workDir, startCmd, transportURL string, env map[string]string) 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
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 with the specified command
// The startCmd parameter allows sessions to use different commands than the adapter default
// The transportURL parameter is for ACP adapter (e.g., "stdio://", "tcp://host:port", "unix:///path")
// Other adapters should ignore this parameter
// The env parameter sets session-level environment variables (merged with adapter-level env)
CreateSession(sessionName, workDir, startCmd, transportURL string, env map[string]string) error
}
CLIAdapter defines the interface for CLI adapters
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 TranscriptMessage ¶
type TranscriptMessage struct {
Type string `json:"type"` // "user", "assistant", "progress", etc.
SessionID string `json:"sessionId"`
IsMeta bool `json:"isMeta"`
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