geminicli

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: May 18, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package geminicli implements the Agent interface for Gemini CLI.

Index

Constants

View Source
const (
	HookNameSessionStart        = "session-start"
	HookNameSessionEnd          = "session-end"
	HookNameBeforeAgent         = "before-agent"
	HookNameAfterAgent          = "after-agent"
	HookNameBeforeModel         = "before-model"
	HookNameAfterModel          = "after-model"
	HookNameBeforeToolSelection = "before-tool-selection"
	HookNameBeforeTool          = "before-tool"
	HookNameAfterTool           = "after-tool"
	HookNamePreCompress         = "pre-compress"
	HookNameNotification        = "notification"
)

Gemini CLI hook names - these become subcommands under `entire hooks gemini`

View Source
const (
	MessageTypeUser   = "user"
	MessageTypeGemini = "gemini"
)

Message type constants for Gemini transcripts

View Source
const (
	ToolWriteFile = "write_file"
	ToolEditFile  = "edit_file"
	ToolSaveFile  = "save_file"
	ToolReplace   = "replace"
)

Tool names used in Gemini CLI that modify files Note: Gemini CLI uses different names in different contexts: - Internal/transcript names: write_file, replace - Display names: WriteFile, Edit

View Source
const GeminiSettingsFileName = "settings.json"

GeminiSettingsFileName is the settings file used by Gemini CLI.

Variables

View Source
var FileModificationTools = []string{
	ToolWriteFile,
	ToolEditFile,
	ToolSaveFile,
	ToolReplace,
}

FileModificationTools lists tools that create or modify files in Gemini CLI

Functions

func ExtractAllUserPrompts

func ExtractAllUserPrompts(data []byte) ([]string, error)

ExtractAllUserPrompts extracts all user messages from transcript data

func ExtractAllUserPromptsFromTranscript

func ExtractAllUserPromptsFromTranscript(transcript *GeminiTranscript) []string

ExtractAllUserPromptsFromTranscript extracts all user prompts from a parsed transcript

func ExtractModifiedFiles

func ExtractModifiedFiles(data []byte) ([]string, error)

ExtractModifiedFiles extracts files modified by tool calls from transcript data

func ExtractModifiedFilesFromTranscript

func ExtractModifiedFilesFromTranscript(transcript *GeminiTranscript) []string

ExtractModifiedFilesFromTranscript extracts files from a parsed transcript

func GetLastMessageID

func GetLastMessageID(data []byte) (string, error)

GetLastMessageID returns the ID of the last message in the transcript. Returns empty string if the transcript is empty or the last message has no ID.

func GetLastMessageIDFromFile

func GetLastMessageIDFromFile(path string) (string, error)

GetLastMessageIDFromFile reads a transcript file and returns the last message's ID. Returns empty string if the file doesn't exist, is empty, or has no messages with IDs.

func GetLastMessageIDFromTranscript

func GetLastMessageIDFromTranscript(transcript *GeminiTranscript) string

GetLastMessageIDFromTranscript returns the ID of the last message in a parsed transcript. Returns empty string if the transcript is empty or the last message has no ID.

func GetProjectHash added in v0.4.6

func GetProjectHash(projectRoot string) string

GetProjectHash generates a unique hash for a project based on its root path. This matches Gemini CLI's getProjectHash() which uses SHA256 of the project root.

func NewGeminiCLIAgent

func NewGeminiCLIAgent() agent.Agent

func NewReviewer added in v0.6.1

func NewReviewer() *reviewtypes.ReviewerTemplate

NewReviewer returns the AgentReviewer for gemini.

Argv shape: gemini -p " " (space placeholder to trigger headless mode). Prompt is piped via stdin; per gemini --help the -p flag appends to stdin content, so passing a single space lets stdin carry the actual prompt. Stdout in this mode is the assistant text directly — parsed line-by-line.

func NormalizeTranscript added in v0.5.3

func NormalizeTranscript(data []byte) ([]byte, error)

NormalizeTranscript normalizes user message content fields in-place from [{"text":"..."}] arrays to plain strings, preserving all other transcript fields (timestamps, thoughts, tokens, model, toolCalls, etc.).

This operates on raw JSON rather than using ParseTranscript + re-marshal because GeminiMessage only captures a subset of fields (id, type, content, toolCalls). Round-tripping through the struct would silently drop fields like timestamp, model, and tokens that are present in real Gemini transcripts. The raw approach rewrites only the content values while leaving all other fields untouched.

func SliceFromMessage added in v0.4.4

func SliceFromMessage(data []byte, startMessageIndex int) ([]byte, error)

SliceFromMessage returns a Gemini transcript scoped to messages starting from startMessageIndex. This is the Gemini equivalent of transcript.SliceFromLine — for Gemini's single JSON blob, scoping is done by message index rather than line offset. Returns the original data if startMessageIndex <= 0. Returns nil, nil if startMessageIndex exceeds the number of messages.

Types

type GeminiCLIAgent

type GeminiCLIAgent struct {
	CommandRunner agent.TextCommandRunner
}

GeminiCLIAgent implements the Agent interface for Gemini CLI.

func (*GeminiCLIAgent) AreHooksInstalled

func (g *GeminiCLIAgent) AreHooksInstalled(ctx context.Context) bool

AreHooksInstalled checks if Entire hooks are installed.

func (*GeminiCLIAgent) CalculateTokenUsage added in v0.4.6

func (g *GeminiCLIAgent) CalculateTokenUsage(transcriptData []byte, fromOffset int) (*agent.TokenUsage, error)

CalculateTokenUsage computes token usage from the transcript starting at the given message offset.

func (*GeminiCLIAgent) ChunkTranscript

func (g *GeminiCLIAgent) ChunkTranscript(ctx context.Context, content []byte, maxSize int) ([][]byte, error)

ChunkTranscript splits a Gemini JSON transcript by distributing messages across chunks. Gemini uses JSON format with a {"messages": [...]} structure, so chunking splits the messages array while preserving the JSON structure in each chunk.

func (*GeminiCLIAgent) Description

func (g *GeminiCLIAgent) Description() string

Description returns a human-readable description.

func (*GeminiCLIAgent) DetectPresence

func (g *GeminiCLIAgent) DetectPresence(ctx context.Context) (bool, error)

DetectPresence checks if Gemini CLI is configured in the repository.

func (*GeminiCLIAgent) DiscoverReviewSkills added in v0.6.1

func (g *GeminiCLIAgent) DiscoverReviewSkills(_ context.Context) ([]agent.DiscoveredSkill, error)

DiscoverReviewSkills is a stub until the Gemini CLI on-disk extension layout is verified. Returns (nil, nil) so the picker relies on the per-agent install hint for Phase 1.

func (*GeminiCLIAgent) ExtractModifiedFilesFromOffset

func (g *GeminiCLIAgent) ExtractModifiedFilesFromOffset(path string, startOffset int) (files []string, currentPosition int, err error)

ExtractModifiedFilesFromOffset extracts files modified since a given message index. For Gemini (JSON format), offset is the starting message index. Returns:

  • files: list of file paths modified by Gemini (from Write/Edit tools)
  • currentPosition: total number of messages in the transcript
  • error: any error encountered during reading

func (*GeminiCLIAgent) FormatResumeCommand

func (g *GeminiCLIAgent) FormatResumeCommand(sessionID string) string

FormatResumeCommand returns the command to resume a Gemini CLI session.

func (*GeminiCLIAgent) GenerateText added in v0.5.6

func (g *GeminiCLIAgent) GenerateText(ctx context.Context, prompt string, model string) (string, error)

GenerateText sends a prompt to the Gemini CLI and returns the raw text response.

The prompt is piped to the Gemini CLI via stdin rather than embedded in argv. Per gemini --help, the -p/--prompt flag is appended to any input read from stdin; we pass a single-space placeholder to trigger headless (non-interactive) mode and let stdin carry the actual content, avoiding argv size limits.

func (*GeminiCLIAgent) GetSessionBaseDir added in v0.5.3

func (g *GeminiCLIAgent) GetSessionBaseDir() (string, error)

GetSessionBaseDir returns the base directory containing per-project session subdirectories. Unlike GetSessionDir, this does NOT use ENTIRE_TEST_GEMINI_PROJECT_DIR because the test override points to a specific project dir, not the base containing all projects.

func (*GeminiCLIAgent) GetSessionDir

func (g *GeminiCLIAgent) GetSessionDir(repoPath string) (string, error)

GetSessionDir returns the directory where Gemini stores session transcripts. Gemini stores sessions in ~/.gemini/tmp/<project-hash>/chats/

func (*GeminiCLIAgent) GetSessionID

func (g *GeminiCLIAgent) GetSessionID(input *agent.HookInput) string

GetSessionID extracts the session ID from hook input.

func (*GeminiCLIAgent) GetTranscriptPosition

func (g *GeminiCLIAgent) GetTranscriptPosition(path string) (int, error)

GetTranscriptPosition returns the current message count of a Gemini transcript. Gemini uses JSON format with a messages array, so position is the message count. Returns 0 if the file doesn't exist or is empty.

func (*GeminiCLIAgent) HookNames added in v0.4.6

func (g *GeminiCLIAgent) HookNames() []string

HookNames returns the hook verbs Gemini CLI supports. These become subcommands: entire hooks gemini <verb>

func (*GeminiCLIAgent) InstallHooks

func (g *GeminiCLIAgent) InstallHooks(ctx context.Context, localDev bool, force bool) (int, error)

InstallHooks installs Gemini CLI hooks in .gemini/settings.json. If force is true, removes existing Entire hooks before installing. Returns the number of hooks installed.

func (*GeminiCLIAgent) IsPreview added in v0.4.6

func (g *GeminiCLIAgent) IsPreview() bool

func (*GeminiCLIAgent) LaunchCmd added in v0.6.1

func (g *GeminiCLIAgent) LaunchCmd(ctx context.Context, initialPrompt string) (*exec.Cmd, error)

LaunchCmd builds an exec.Cmd for `gemini "<initialPrompt>"`. Stdio is wired to the caller's TTY so the agent runs foreground and the user interacts normally. The call site is expected to Run() and wait. Hooks inherit the parent environment.

func (*GeminiCLIAgent) Name

func (g *GeminiCLIAgent) Name() types.AgentName

Name returns the agent registry key.

func (*GeminiCLIAgent) ParseHookEvent added in v0.4.6

func (g *GeminiCLIAgent) ParseHookEvent(_ context.Context, hookName string, stdin io.Reader) (*agent.Event, error)

ParseHookEvent translates a Gemini CLI hook into a normalized lifecycle Event. Returns nil if the hook has no lifecycle significance (e.g., pass-through hooks).

func (*GeminiCLIAgent) ProtectedDirs added in v0.4.3

func (g *GeminiCLIAgent) ProtectedDirs() []string

ProtectedDirs returns directories that Gemini uses for config/state.

func (*GeminiCLIAgent) ReadSession

func (g *GeminiCLIAgent) ReadSession(input *agent.HookInput) (*agent.AgentSession, error)

ReadSession reads a session from Gemini's storage (JSON transcript file). The session data is stored in NativeData as raw JSON bytes.

func (*GeminiCLIAgent) ReadTranscript added in v0.4.6

func (g *GeminiCLIAgent) ReadTranscript(sessionRef string) ([]byte, error)

ReadTranscript reads the raw JSON transcript bytes for a session.

func (*GeminiCLIAgent) ReassembleTranscript

func (g *GeminiCLIAgent) ReassembleTranscript(chunks [][]byte) ([]byte, error)

ReassembleTranscript merges Gemini JSON chunks by combining their message arrays.

func (*GeminiCLIAgent) ResolveSessionFile added in v0.4.3

func (g *GeminiCLIAgent) ResolveSessionFile(sessionDir, agentSessionID string) string

ResolveSessionFile returns the path to a Gemini session file. Gemini names files as session-<date>-<shortid>.json where shortid is the first 8 chars of the session UUID. This searches for an existing file matching the pattern, falling back to constructing a filename matching Gemini's convention if no match is found.

func (*GeminiCLIAgent) Type

func (g *GeminiCLIAgent) Type() types.AgentType

Type returns the agent type identifier.

func (*GeminiCLIAgent) UninstallHooks

func (g *GeminiCLIAgent) UninstallHooks(ctx context.Context) error

UninstallHooks removes Entire hooks from Gemini CLI settings.

func (*GeminiCLIAgent) WriteHookResponse added in v0.5.1

func (g *GeminiCLIAgent) WriteHookResponse(message string) error

WriteHookResponse outputs a hook response message as plain text to stdout.

Why plain text and not JSON? Gemini CLI (as of v0.40.0) double-displays systemMessage when it arrives in JSON form: once via emitHookSystemMessage (rendered with the [hookName] source tag) and again via the SessionStart path's direct historyManager.addItem (rendered without a tag). With plain text, gemini's convertPlainTextToHookOutput synthesizes a systemMessage internally, the JSON-only emitHookSystemMessage event doesn't fire, and the user sees the banner exactly once.

func (*GeminiCLIAgent) WriteSession

func (g *GeminiCLIAgent) WriteSession(_ context.Context, session *agent.AgentSession) error

WriteSession writes a session to Gemini's storage (JSON transcript file). Uses the NativeData field which contains raw JSON bytes.

type GeminiHookEntry

type GeminiHookEntry struct {
	Name    string `json:"name"`
	Type    string `json:"type"`
	Command string `json:"command"`
}

GeminiHookEntry represents a single hook command. Unlike Claude Code, Gemini CLI requires a "name" field for each hook entry.

type GeminiHookMatcher

type GeminiHookMatcher struct {
	Matcher string            `json:"matcher,omitempty"`
	Hooks   []GeminiHookEntry `json:"hooks"`
}

GeminiHookMatcher matches hooks to specific patterns

type GeminiHooks

type GeminiHooks struct {
	// Hooks are only executed when hooksConfig.enabled is true in .gemini/settings.json.
	SessionStart        []GeminiHookMatcher `json:"SessionStart,omitempty"`
	SessionEnd          []GeminiHookMatcher `json:"SessionEnd,omitempty"`
	BeforeAgent         []GeminiHookMatcher `json:"BeforeAgent,omitempty"`
	AfterAgent          []GeminiHookMatcher `json:"AfterAgent,omitempty"`
	BeforeModel         []GeminiHookMatcher `json:"BeforeModel,omitempty"`
	AfterModel          []GeminiHookMatcher `json:"AfterModel,omitempty"`
	BeforeToolSelection []GeminiHookMatcher `json:"BeforeToolSelection,omitempty"`
	BeforeTool          []GeminiHookMatcher `json:"BeforeTool,omitempty"`
	AfterTool           []GeminiHookMatcher `json:"AfterTool,omitempty"`
	PreCompress         []GeminiHookMatcher `json:"PreCompress,omitempty"`
	Notification        []GeminiHookMatcher `json:"Notification,omitempty"`
}

GeminiHooks contains all hook configurations

type GeminiHooksConfig

type GeminiHooksConfig struct {
	Enabled bool `json:"enabled,omitempty"`
}

GeminiHooksConfig contains tool-related settings

type GeminiMessage

type GeminiMessage struct {
	ID        string           `json:"id,omitempty"` // UUID for the message
	Type      string           `json:"type"`         // MessageTypeUser or MessageTypeGemini
	Content   string           `json:"content,omitempty"`
	ToolCalls []GeminiToolCall `json:"toolCalls,omitempty"`
}

GeminiMessage represents a single message in the transcript

func (*GeminiMessage) UnmarshalJSON added in v0.4.5

func (m *GeminiMessage) UnmarshalJSON(data []byte) error

UnmarshalJSON handles both string and array content formats in Gemini transcripts. User messages use: "content": [{"text": "..."}] (array of objects) Gemini messages use: "content": "response text" (string)

type GeminiSettings

type GeminiSettings struct {
	HooksConfig GeminiHooksConfig `json:"hooksConfig,omitempty"`
	Hooks       GeminiHooks       `json:"hooks,omitempty"`
}

GeminiSettings represents the .gemini/settings.json structure

type GeminiToolCall

type GeminiToolCall struct {
	ID     string                 `json:"id"`
	Name   string                 `json:"name"`
	Args   map[string]interface{} `json:"args"`
	Status string                 `json:"status,omitempty"`
}

GeminiToolCall represents a tool call in a gemini message

type GeminiTranscript

type GeminiTranscript struct {
	Messages []GeminiMessage `json:"messages"`
}

GeminiTranscript represents the top-level structure of a Gemini session file

func ParseTranscript

func ParseTranscript(data []byte) (*GeminiTranscript, error)

ParseTranscript parses raw JSON content into a transcript structure

Jump to

Keyboard shortcuts

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