Documentation
¶
Overview ¶
Package antigravity implements the Agent interface for Antigravity (Google's agentic coding CLI).
Index ¶
- Constants
- Variables
- func AppendStatusSnapshot(payload []byte) error
- func InstallTitleTee() error
- func NewAntigravityAgent() agent.Agent
- func TitleTeeInstalled() bool
- func UninstallTitleTee() error
- type AntigravityAgent
- func (a *AntigravityAgent) AreHooksInstalled(ctx context.Context) (bool, error)
- func (a *AntigravityAgent) CalculateTokenUsageSince(ctx context.Context, sessionID string, baseline json.RawMessage) (*agent.TokenUsage, error)
- func (a *AntigravityAgent) ChunkTranscript(_ context.Context, content []byte, maxSize int) ([][]byte, error)
- func (a *AntigravityAgent) CountTranscriptPosition(content []byte) int
- func (a *AntigravityAgent) Description() string
- func (a *AntigravityAgent) DetectPresence(ctx context.Context) (bool, error)
- func (a *AntigravityAgent) DiscoverReviewSkills(ctx context.Context) ([]agent.DiscoveredSkill, error)
- func (a *AntigravityAgent) ExtractModifiedFilesFromOffset(ctx context.Context, path string, startOffset int) (files []string, currentPosition int, err error)
- func (a *AntigravityAgent) ExtractPrompts(sessionRef string, fromOffset int) ([]string, error)
- func (a *AntigravityAgent) ExtractPromptsFromTranscript(content []byte, fromOffset int) ([]string, error)
- func (a *AntigravityAgent) FormatResumeCommand(sessionID string) string
- func (a *AntigravityAgent) GenerateText(ctx context.Context, prompt string, model string) (string, error)
- func (a *AntigravityAgent) GetSessionDir(_ string) (string, error)
- func (a *AntigravityAgent) GetSessionID(input *agent.HookInput) string
- func (a *AntigravityAgent) GetTranscriptPosition(path string) (int, error)
- func (a *AntigravityAgent) HookConfigRelPath() string
- func (a *AntigravityAgent) HookNames() []string
- func (a *AntigravityAgent) HooksDisabled(ctx context.Context) (bool, error)
- func (a *AntigravityAgent) HooksEntryMatchesHost(ctx context.Context) (installed, current bool, err error)
- func (a *AntigravityAgent) InstallHooks(ctx context.Context, force bool) (int, error)
- func (a *AntigravityAgent) Name() types.AgentName
- func (a *AntigravityAgent) ParseHookEvent(_ context.Context, hookName string, stdin io.Reader) (*agent.Event, error)
- func (a *AntigravityAgent) PrepareTranscript(ctx context.Context, transcriptRef string) error
- func (a *AntigravityAgent) ProtectedDirs() []string
- func (a *AntigravityAgent) ReadSession(_ *agent.HookInput) (*agent.AgentSession, error)
- func (a *AntigravityAgent) ReadTranscript(sessionRef string) ([]byte, error)
- func (a *AntigravityAgent) ReassembleTranscript(chunks [][]byte) ([]byte, error)
- func (a *AntigravityAgent) ResolveSessionFile(sessionDir, agentSessionID string) string
- func (a *AntigravityAgent) SliceTranscriptFromPosition(content []byte, startOffset int) []byte
- func (a *AntigravityAgent) SnapshotTokenBaseline(ctx context.Context, sessionID string) (json.RawMessage, error)
- func (a *AntigravityAgent) Type() types.AgentType
- func (a *AntigravityAgent) UninstallHooks(ctx context.Context) error
- func (a *AntigravityAgent) WriteSession(_ context.Context, session *agent.AgentSession) error
- type CommonPayload
- type CondensedStep
- type HookCommand
- type HookConfig
- type HooksFile
- type HooksProbe
- type InvocationPayload
- type PreToolUsePayload
- type SimpleHandler
- type StopPayload
- type ToolCall
- type ToolHandler
Constants ¶
const ( HookNamePreToolUse = "pre-tool-use" HookNamePreInvocation = "pre-invocation" HookNameStop = "stop" )
Antigravity hook name constants — these become subcommands under `entire hooks antigravity`.
Only the hooks with lifecycle significance are installed. agy also offers PostToolUse and PostInvocation, but neither maps to an Entire lifecycle event (PostInvocation fires before the transcript is written; PostToolUse duplicates what PreToolUse already captures) — installing them would spawn a no-op `entire` subprocess on every completed tool call and model invocation.
const ( CondensedRoleUser = "user" CondensedRoleAssistant = "assistant" CondensedRoleTool = "tool" )
Roles of a CondensedStep.
const AgentsHooksFileName = "hooks.json"
AgentsHooksFileName is the hooks file used by Antigravity.
const DoctorProbeEnv = "ENTIRE_ANTIGRAVITY_DOCTOR_PROBE"
DoctorProbeEnv opts `entire doctor` into running ProbeLoadedHooks. Off by default: the probe's zero-quota argument rested on the version gate alone, and on Windows 11 with agy 1.2.7 `agy -p "/hooks"` was observed to run a full model turn (~12k input tokens) instead of answering locally. Until the local-answer behaviour is verified per platform rather than assumed from a version number, a default doctor run must not risk the user's quota. The probe also only proves agy PARSED hooks.json, not that the command in it can run; HooksEntryMatchesHost is the check that catches the failure that actually occurs.
const MinHooksProbeVersion = "1.1.12"
MinHooksProbeVersion is the first agy release whose print mode answers the read-only `/hooks` slash command locally — one tab-separated record per hook (or a structured payload under --output-format json) — "without starting an agent turn, spending quota or leaving a conversation behind" (agy 1.1.12 release notes). On older releases `-p "/hooks"` is sent to the model as literal prompt text, so the probe must never run there.
Variables ¶
var ErrHooksProbeUnsupported = errors.New("agy too old to answer /hooks in print mode")
ErrHooksProbeUnsupported is returned when the installed agy predates MinHooksProbeVersion; callers should report "cannot verify" rather than "not loaded".
var ErrHooksProbeVersionUnknown = errors.New("could not determine the agy version")
ErrHooksProbeVersionUnknown is returned when `agy --version` printed something semver cannot parse (a build suffix, a banner line). It is deliberately distinct from ErrHooksProbeUnsupported: the agy may well be newer than the requirement, so "run `agy update`" would be wrong advice. The probe is skipped either way — a wrong guess is a real model turn.
Functions ¶
func AppendStatusSnapshot ¶
AppendStatusSnapshot parses an agy state-JSON payload and appends a snapshot to the per-conversation JSONL file. The hot path never returns an error for malformed input — only for genuine I/O failures.
agy fires the title command on every agent state change and does not serialize the invocations, so two tees can run at once. The dedup read and the append therefore happen under one advisory lock per conversation (<id>.jsonl.lock, next to the file); without it a concurrent tee could append between the read and the write and the dedup would miss.
func InstallTitleTee ¶
func InstallTitleTee() error
InstallTitleTee installs the title-tee shim into agy's global settings.json. If a user's own title command is already present, it is preserved via --wrap. The call is idempotent: if our marker is already in the command, it returns nil.
func NewAntigravityAgent ¶
NewAntigravityAgent creates a new AntigravityAgent instance.
func TitleTeeInstalled ¶
func TitleTeeInstalled() bool
TitleTeeInstalled reports whether agy's global settings.json declares a title command containing the title-tee marker. It is used by `entire doctor` to warn when Antigravity hooks are installed in a repo but the global title slot — agy's only token-usage surface — has not been claimed, which would leave token counts missing from checkpoints. A missing or unparseable settings file reports false.
func UninstallTitleTee ¶
func UninstallTitleTee() error
UninstallTitleTee removes or restores the title entry in agy's global settings.json:
- bare tee (no --wrap) → delete "title" key
- tee with --wrap 'X' → restore X
- any other (foreign) cmd → leave untouched
- missing settings file → no-op
Types ¶
type AntigravityAgent ¶
type AntigravityAgent struct {
CommandRunner agent.TextCommandRunner
// contains filtered or unexported fields
}
AntigravityAgent implements the Agent interface for Antigravity.
func (*AntigravityAgent) AreHooksInstalled ¶
func (a *AntigravityAgent) AreHooksInstalled(ctx context.Context) (bool, error)
AreHooksInstalled checks if Entire hooks are installed.
func (*AntigravityAgent) CalculateTokenUsageSince ¶
func (a *AntigravityAgent) CalculateTokenUsageSince(ctx context.Context, sessionID string, baseline json.RawMessage) (*agent.TokenUsage, error)
CalculateTokenUsageSince computes the delta between the baseline snapshot and the latest persisted snapshot.
Exact: InputTokens/OutputTokens (cumulative totals minus baseline totals). Best-effort: cache fields and APICallCount, derived from the snapshot lines appended after the baseline timestamp (the dedup writer appends ~one line per API response, but lines can be missed between agent state changes).
func (*AntigravityAgent) ChunkTranscript ¶
func (*AntigravityAgent) CountTranscriptPosition ¶
func (a *AntigravityAgent) CountTranscriptPosition(content []byte) int
CountTranscriptPosition implements agent.LateTranscriptWriter: agy writes its transcript only after the Stop hook, and its offset metric counts non-blank lines. Delegating to forEachNonBlankLine keeps this byte-identical with GetTranscriptPosition/ExtractPrompts (see the iterator's doc comment).
func (*AntigravityAgent) Description ¶
func (a *AntigravityAgent) Description() string
func (*AntigravityAgent) DetectPresence ¶
func (a *AntigravityAgent) DetectPresence(ctx context.Context) (bool, error)
DetectPresence reports whether Entire's Antigravity hooks are configured for this workspace. Antigravity 2.0 stores runtime data user-scope in ~/.gemini/antigravity-cli/, so the only meaningful workspace-level signal is whether our entry exists in .agents/hooks.json.
func (*AntigravityAgent) DiscoverReviewSkills ¶
func (a *AntigravityAgent) DiscoverReviewSkills(ctx context.Context) ([]agent.DiscoveredSkill, error)
DiscoverReviewSkills walks Antigravity's three user-skill scopes looking for review-adjacent skills. agy stores skills as <scope>/<name>/SKILL.md with YAML frontmatter (name + description); the model activates them by description, and they are referenced with a "/name" slash invocation.
Scopes, in precedence order (global wins on name collision):
- global (agy 1.1+): ~/.gemini/config/skills
- global (pre-1.1 layouts): ~/.gemini/antigravity-cli/skills, ~/.gemini/skills
- workspace (agy 1.1+): <repo>/.agents/skills
- workspace (legacy, honored): <repo>/.agent/skills
agy 1.1 moved the defaults ("Antigravity now defaults to .agents/skills, but still maintains backward support for .agent/skills"; global discovery under ~/.gemini/config/) — all roots are scanned so skills keep resolving across agy versions.
Google's built-in skills under ~/.gemini/antigravity-cli/builtin/skills are deliberately NOT scanned — they are shipped guides (e.g. antigravity-guide), not user review tools.
Best-effort per the SkillDiscoverer contract: unreadable HOME or missing directories yield (nil, nil); malformed SKILL.md files are skipped by the shared scanner with a Debug log.
func (*AntigravityAgent) ExtractModifiedFilesFromOffset ¶
func (a *AntigravityAgent) ExtractModifiedFilesFromOffset(ctx context.Context, path string, startOffset int) (files []string, currentPosition int, err error)
ExtractModifiedFilesFromOffset implements agent.TranscriptAnalyzer. It scans agy step lines after startOffset for mutating tool calls and returns the target file paths they touch, deduplicated, alongside the new line position.
Path convention: returned paths are ABSOLUTE and symlink-resolved — the same shape lifecycle.go's parsePreToolUse records into FilesTouched. The framework relativizes downstream via FilterAndNormalizePaths -> paths.ToRelativePath against the worktree root, so we must NOT pre-relativize here. We mirror parsePreToolUse exactly: decode the double-encoded TargetFile arg, then resolveAgySymlinks so the path matches what attribution diffs against (e.g. macOS /tmp -> /private/tmp). Both helpers live in lifecycle.go (same package) and are reused, not duplicated.
The blank-skip -> lineNum++ -> (lineNum <= startOffset) ordering matches ExtractPrompts so positions stay consistent across analyzer methods.
func (*AntigravityAgent) ExtractPrompts ¶
func (a *AntigravityAgent) ExtractPrompts(sessionRef string, fromOffset int) ([]string, error)
ExtractPrompts implements agent.PromptExtractor. agy's PreInvocation hook carries no prompt, so the user prompt is recovered from the transcript's USER_INPUT steps. fromOffset is a count of non-blank lines already consumed.
func (*AntigravityAgent) ExtractPromptsFromTranscript ¶
func (a *AntigravityAgent) ExtractPromptsFromTranscript(content []byte, fromOffset int) ([]string, error)
ExtractPromptsFromTranscript implements agent.TranscriptPromptExtractor over transcript bytes the caller already holds — condensation's copy of the transcript — with the same offset metric as ExtractPrompts. It never reads a path, so the prompts it returns always describe the bytes being checkpointed.
func (*AntigravityAgent) FormatResumeCommand ¶
func (a *AntigravityAgent) FormatResumeCommand(sessionID string) string
func (*AntigravityAgent) GenerateText ¶
func (a *AntigravityAgent) GenerateText(ctx context.Context, prompt string, model string) (string, error)
GenerateText submits a non-interactive prompt to the Antigravity CLI. The binary is `agy`; -p is the short alias for --print (single-prompt mode).
The prompt travels in argv. Earlier releases accepted it on stdin behind a single-space -p placeholder (the Gemini CLI convention, verified on agy 1.0.16), but agy 1.2.7 ignores stdin in print mode: `-p " "` fails with "Error: empty prompt", and `-p -` is answered as the literal message "-" (both observed live, trail 444, 2026-09-22), which is how `entire dispatch --local --agent antigravity` came to hand agy an empty prompt. argv is the only documented route ("Usage: agy --print 'your prompt here'").
That makes prompt size this agent's problem in a way it is not for agents that use RunIsolatedTextGeneratorCLI's stdin. Linux caps a SINGLE argument at MAX_ARG_STRLEN (128 KiB) however large the total ARG_MAX is, so an unbounded prompt fails with E2BIG. summarize.maxCondensedTranscriptBytes is what keeps summary prompts inside it. Windows' ~32 KiB whole-command-line limit is tighter than any useful transcript budget and is not covered; a long enough prompt still fails there, loudly.
agy also runs with a home directory of its own (isolatedHomeEnv). It reads its window-title command (~/.gemini/antigravity-cli/settings.json), its PreInvocation hooks (~/.gemini/config/hooks.json), MCP servers, and skills from the user's home, and print mode executes the title command and the hooks (observed live on agy 1.3.1). The empty working directory keeps workspace configuration out but not that, and agy has no flag to skip it, so a summary run, whose prompt carries untrusted transcript content, would otherwise execute whatever the user's global configuration names.
func (*AntigravityAgent) GetSessionDir ¶
func (a *AntigravityAgent) GetSessionDir(_ string) (string, error)
func (*AntigravityAgent) GetSessionID ¶
func (a *AntigravityAgent) GetSessionID(input *agent.HookInput) string
func (*AntigravityAgent) GetTranscriptPosition ¶
func (a *AntigravityAgent) GetTranscriptPosition(path string) (int, error)
GetTranscriptPosition implements agent.TranscriptAnalyzer. It returns the number of non-blank JSONL lines in the transcript, which the framework uses as a stable offset to bound subsequent extraction to a single checkpoint range. A missing file yields (0, nil) so a not-yet-flushed transcript (agy writes asynchronously) doesn't fail the hook.
func (*AntigravityAgent) HookConfigRelPath ¶
func (a *AntigravityAgent) HookConfigRelPath() string
HookConfigRelPath implements agent.HookConfigLocator: agy loads workspace hooks from .agents/hooks.json at the worktree root.
func (*AntigravityAgent) HookNames ¶
func (a *AntigravityAgent) HookNames() []string
HookNames returns the hook verbs Antigravity supports. These become subcommands: entire hooks antigravity <verb>
func (*AntigravityAgent) HooksDisabled ¶
func (a *AntigravityAgent) HooksDisabled(ctx context.Context) (bool, error)
HooksDisabled reports whether the repo's "entire" entry is present but explicitly switched off with "enabled": false. InstallHooks already treats that as a deliberate opt-out and leaves the entry alone.
It exists so `entire doctor` can stay quiet about a configuration the user turned off. AreHooksInstalled and DetectPresence deliberately still report such an entry as present: they drive agent auto-detection and `entire agent list`, which describe what is on disk.
func (*AntigravityAgent) HooksEntryMatchesHost ¶
func (a *AntigravityAgent) HooksEntryMatchesHost(ctx context.Context) (installed, current bool, err error)
HooksEntryMatchesHost reports whether the repo's installed "entire" entry is exactly what InstallHooks would write on THIS host. installed is false when there is no entry. A user-disabled entry counts as current.
It exists for `entire doctor`: the hook command's SHAPE is host-specific (agy runs it through cmd.exe on Windows and sh elsewhere), and a hooks.json committed from a macOS checkout carries a sh wrapper that cmd.exe tears apart — the hook exits 1, the failure shows only in agy's log, and nothing is tracked. A file that merely exists proves nothing about that; comparing against the candidate does, at zero cost and without spawning agy.
func (*AntigravityAgent) InstallHooks ¶
InstallHooks installs Antigravity hooks in .agents/hooks.json. If force is true, removes existing Entire hooks before installing. Returns the number of hooks installed.
func (*AntigravityAgent) Name ¶
func (a *AntigravityAgent) Name() types.AgentName
func (*AntigravityAgent) ParseHookEvent ¶
func (a *AntigravityAgent) ParseHookEvent(_ context.Context, hookName string, stdin io.Reader) (*agent.Event, error)
ParseHookEvent translates an Antigravity hook into a normalized lifecycle Event. Returns nil if the hook has no lifecycle significance.
func (*AntigravityAgent) PrepareTranscript ¶
func (a *AntigravityAgent) PrepareTranscript(ctx context.Context, transcriptRef string) error
PrepareTranscript implements the optional TranscriptPreparer interface. The framework calls this in handleLifecycleTurnEnd BEFORE its fileExists check — so we use it to handle agy's asynchronous transcript write.
Background: agy writes its transcript file at
~/.gemini/antigravity-cli/brain/<conv-id>/.system_generated/logs/transcript_full.jsonl
AFTER the Stop hook fires (sometimes seconds later, depending on session shutdown timing). Our TurnEnd event maps to Stop, so we routinely race the transcript write. Without PrepareTranscript, the framework's fileExists check fails with "transcript file not found" and our hook returns exit 1, terminating agy's agent turn.
We briefly wait for the real transcript first. If it is still missing, we materialise an empty placeholder. files_touched is already captured via the PreToolUse hook (independent of transcript content), so condensation can still produce a meaningful checkpoint from an empty transcript.
The placeholder is a WRITE on a hook-supplied path, so it is created only inside agy's brain directory (the agent's SessionStore): parents with MkdirAllNoSymlink, the file with O_EXCL through the root, and a path outside that directory refused rather than anchored on its own parent (docs/development/filesystem-safety.md, "The Root Anchors"). Tests place transcripts under ENTIRE_TEST_ANTIGRAVITY_BRAIN_DIR for the same reason.
func (*AntigravityAgent) ProtectedDirs ¶
func (a *AntigravityAgent) ProtectedDirs() []string
func (*AntigravityAgent) ReadSession ¶
func (a *AntigravityAgent) ReadSession(_ *agent.HookInput) (*agent.AgentSession, error)
func (*AntigravityAgent) ReadTranscript ¶
func (a *AntigravityAgent) ReadTranscript(sessionRef string) ([]byte, error)
ReadTranscript reads a transcript agy's hook payload named. When the path is inside agy's brain directory — where every real payload points — the read goes through the agent's SessionStore: a name inside a trusted root, no symlink followed at any component. A path outside it is the read-side gap docs/development/filesystem-safety.md describes (closing it needs RepoPath on HookInput), and it stays the one unconfined read the ratchet in agent/transcript_read_guard_test.go allows this file; it is never anchored on the path's own parent, which would contain nothing while looking like it did.
func (*AntigravityAgent) ReassembleTranscript ¶
func (a *AntigravityAgent) ReassembleTranscript(chunks [][]byte) ([]byte, error)
func (*AntigravityAgent) ResolveSessionFile ¶
func (a *AntigravityAgent) ResolveSessionFile(sessionDir, agentSessionID string) string
func (*AntigravityAgent) SliceTranscriptFromPosition ¶
func (a *AntigravityAgent) SliceTranscriptFromPosition(content []byte, startOffset int) []byte
SliceTranscriptFromPosition implements agent.LateTranscriptWriter. It scopes content the same way CountTranscriptPosition counts it, through the one iterator that owns the metric. transcript.SliceFromLine cannot stand in: it counts raw \n-delimited lines, so a single interior blank line puts the summary's window a line off from the offset that was stored for it.
func (*AntigravityAgent) SnapshotTokenBaseline ¶
func (a *AntigravityAgent) SnapshotTokenBaseline(ctx context.Context, sessionID string) (json.RawMessage, error)
SnapshotTokenBaseline returns the latest persisted snapshot for the conversation, or nil if none exists yet. A nil baseline is exact only for a genuinely fresh conversation; a resumed conversation whose title-tee shim hasn't written a snapshot before the first TurnStart will over-count the prior cumulative total on that first tracked turn.
func (*AntigravityAgent) Type ¶
func (a *AntigravityAgent) Type() types.AgentType
func (*AntigravityAgent) UninstallHooks ¶
func (a *AntigravityAgent) UninstallHooks(ctx context.Context) error
UninstallHooks removes the Entire hook entry from .agents/hooks.json.
func (*AntigravityAgent) WriteSession ¶
func (a *AntigravityAgent) WriteSession(_ context.Context, session *agent.AgentSession) error
type CommonPayload ¶
type CommonPayload struct {
ConversationID string `json:"conversationId"`
TranscriptPath string `json:"transcriptPath"`
}
CommonPayload contains the system metadata fields the integration consumes from every hook payload.
type CondensedStep ¶
type CondensedStep struct {
// Role is one of the CondensedRole* constants.
Role string
// Text is the user request (unwrapped from <USER_REQUEST>) or the
// assistant's text; empty for tool steps.
Text string
// ToolName and ToolArgs describe a tool call; ToolArgs values are decoded
// from agy's double-encoded JSON strings where possible.
ToolName string
ToolArgs map[string]any
}
CondensedStep is one summarizable unit of an agy transcript. It exists for the summarizer (cmd/entire/cli/summarize), which owns the prompt shape but not agy's wire format: without it, agy transcripts fell through to the Claude JSONL parser, condensed to nothing, and `explain --generate` reported "transcript has no content to summarize" for a 2.4 KB transcript.
func CondenseTranscript ¶
func CondenseTranscript(content []byte) []CondensedStep
CondenseTranscript reduces agy step JSONL to user requests, assistant text and tool calls, in order. GENERIC steps (tool output) and SYSTEM_MESSAGE steps (agy's own injected notices) are skipped; malformed lines are skipped.
type HookCommand ¶
type HookCommand struct {
Type string `json:"type,omitempty"`
Command string `json:"command"`
Timeout int `json:"timeout,omitempty"`
}
HookCommand is a single executable hook command.
type HookConfig ¶
type HookConfig struct {
Enabled *bool `json:"enabled,omitempty"`
PreToolUse []ToolHandler `json:"PreToolUse,omitempty"`
PostToolUse []ToolHandler `json:"PostToolUse,omitempty"`
PreInvocation []SimpleHandler `json:"PreInvocation,omitempty"`
PostInvocation []SimpleHandler `json:"PostInvocation,omitempty"`
Stop []SimpleHandler `json:"Stop,omitempty"`
}
HookConfig defines the event handlers for a named hook entry. It mirrors agy's hooks.json schema (https://antigravity.google/docs — Hooks), including event keys we don't install (PostToolUse/PostInvocation) so round-tripping a user's file never drops data and the install idempotency comparison detects stale Entire entries that still carry them.
type HooksFile ¶
type HooksFile = map[string]HookConfig
HooksFile maps hook names to their event configurations. The top-level key is the hook name (user-defined, e.g. "my-linter-hook").
type HooksProbe ¶
type HooksProbe struct {
// Version is the agy version string reported by `agy --version`.
Version string
// Loaded is true when agy lists an Entire hook entry sourced from the
// workspace's .agents/hooks.json.
Loaded bool
// Sources are the hooks.json paths agy reported, for diagnostics.
Sources []string
}
HooksProbe is the outcome of asking agy which hooks it actually loads for a workspace. Loaded distinguishes "agy sees Entire's entry" from "the file is on disk but agy ignores it" — the latter is the untrusted-workspace trap (agy resolves an untrusted cwd to its scratch workspace, so no hooks fire).
func ProbeLoadedHooks ¶
func ProbeLoadedHooks(ctx context.Context, repoRoot string) (HooksProbe, error)
ProbeLoadedHooks asks the agy binary on PATH which hooks it loads for repoRoot, via `agy -p /hooks --add-dir <repoRoot> --output-format json`. --add-dir is what makes agy treat repoRoot as the workspace (the same flag the e2e harness relies on); without it the probe would answer for agy's scratch workspace and always report nothing loaded.
Zero-quota by construction: the version gate guarantees agy answers /hooks locally. Returns ErrHooksProbeUnsupported for older agy, and any spawn or parse failure otherwise (an unauthenticated agy fails print mode with "authentication required", which surfaces here as an error).
type InvocationPayload ¶
type InvocationPayload struct {
CommonPayload
InvocationNum int `json:"invocationNum"`
}
InvocationPayload is the stdin payload for the PreInvocation hook.
invocationNum is 0-indexed (the first model invocation of a conversation is 0). initialNumSteps is deliberately not decoded: agy inserts the user prompt as a step before the first model call, so it is already 1 on the first invocation and unusable as a "first?" signal.
type PreToolUsePayload ¶
type PreToolUsePayload struct {
CommonPayload
ToolCall ToolCall `json:"toolCall"`
}
PreToolUsePayload is the stdin payload for the PreToolUse hook.
type SimpleHandler ¶
type SimpleHandler struct {
Type string `json:"type,omitempty"`
Command string `json:"command"`
Timeout int `json:"timeout,omitempty"`
}
SimpleHandler is a direct handler entry used for PreInvocation, PostInvocation, and Stop.
type StopPayload ¶
type StopPayload struct {
CommonPayload
FullyIdle bool `json:"fullyIdle"` // Required
}
StopPayload is the stdin payload for the Stop hook.
type ToolCall ¶
type ToolCall struct {
Name string `json:"name"`
Args json.RawMessage `json:"args"`
}
ToolCall represents a proposed or completed tool invocation.
type ToolHandler ¶
type ToolHandler struct {
Matcher string `json:"matcher,omitempty"`
Hooks []HookCommand `json:"hooks,omitempty"`
}
ToolHandler is a matcher + handlers entry used for PreToolUse / PostToolUse.