Documentation
¶
Overview ¶
Package agenthooks lets you author coding-agent hooks once and run them on Claude Code, Cursor, OpenAI Codex, Gemini CLI, OpenCode, and Kimi Code.
A hook program is a normal Go binary: register typed handlers on a Runner and hand control to Main. The library detects the invoking provider, decodes stdin, dispatches, and encodes the response in the provider's dialect — including exit code and stderr discipline. Consumers never see the wire; they see typed events and return typed decisions.
func main() {
r := agenthooks.New(agenthooks.WithPolicy(agenthooks.Policy{
Fail: agenthooks.FailClosed,
AskFallback: agenthooks.FallbackDeny,
}))
r.OnToolPre(func(ctx context.Context, e *agenthooks.ToolPreEvent) (agenthooks.ToolPreDecision, error) {
if e.Tool.Canonical == agenthooks.ToolShell && isDestructive(e.Tool) {
return agenthooks.Deny("blocked by policy"), nil
}
return agenthooks.NoDecision(), nil
})
agenthooks.Main(r)
}
Index ¶
- Constants
- Variables
- func CompileMatcher(p Provider, m ToolMatcher) (expr string, ok bool)
- func Logger(ctx context.Context) *slog.Logger
- func Main(r *Runner)
- func SynthesizeToolID(sessionID, turnID, toolName string, input []byte) string
- type AgentInfo
- type AskFallback
- type CanonicalTool
- type CapSet
- type Capability
- type CompactEvent
- type DegradeMode
- type DetectionConfidence
- type Event
- type EventKind
- type FailMode
- type FileEditedEvent
- type MCPCall
- type ModelEvent
- type NotificationEvent
- type Option
- type PermissionEvent
- type Policy
- type PolicyFunc
- type PromptDecision
- type PromptEvent
- type Provider
- type Quirk
- type Runner
- func (r *Runner) OnAny(fn func(context.Context, *Event) error)
- func (r *Runner) OnCompactPost(fn func(context.Context, *CompactEvent) error)
- func (r *Runner) OnCompactPre(fn func(context.Context, *CompactEvent) error)
- func (r *Runner) OnFileEdited(fn func(context.Context, *FileEditedEvent) error)
- func (r *Runner) OnModelRequest(fn func(context.Context, *ModelEvent) error)
- func (r *Runner) OnModelResponse(fn func(context.Context, *ModelEvent) error)
- func (r *Runner) OnNotification(fn func(context.Context, *NotificationEvent) error)
- func (r *Runner) OnOther(nativeName string, fn func(context.Context, *Event) error)
- func (r *Runner) OnPermission(fn func(context.Context, *PermissionEvent) (ToolPreDecision, error))
- func (r *Runner) OnPromptSubmitted(fn func(context.Context, *PromptEvent) (PromptDecision, error))
- func (r *Runner) OnSessionEnd(fn func(context.Context, *SessionEndEvent) error)
- func (r *Runner) OnSessionStart(fn func(context.Context, *SessionStartEvent) (SessionStartDecision, error))
- func (r *Runner) OnStop(fn func(context.Context, *StopEvent) (StopDecision, error))
- func (r *Runner) OnSubagentStart(fn func(context.Context, *SubagentStartEvent) error)
- func (r *Runner) OnSubagentStop(fn func(context.Context, *StopEvent) (StopDecision, error))
- func (r *Runner) OnToolError(fn func(context.Context, *ToolPostEvent) (ToolPostDecision, error))
- func (r *Runner) OnToolPost(fn func(context.Context, *ToolPostEvent) (ToolPostDecision, error))
- func (r *Runner) OnToolPre(fn func(context.Context, *ToolPreEvent) (ToolPreDecision, error))
- func (r *Runner) Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) int
- type SessionEndEvent
- type SessionInfo
- type SessionStartDecision
- type SessionStartEvent
- type StopDecision
- type StopEvent
- type SubagentStartEvent
- type ToolCall
- type ToolMatcher
- type ToolPostDecision
- type ToolPostEvent
- type ToolPreDecision
- type ToolPreEvent
- type Usage
- type Variant
Constants ¶
const DefaultContinuationCap = 5
DefaultContinuationCap bounds ContinueWith loops on providers without a native cap (Cursor Claude-compat mode ships loop_limit: null).
Variables ¶
var ErrLossyUpdate = errors.New("agenthooks: updated input removes keys; provider merge is shallow (lossy)")
ErrLossyUpdate is returned when an input rewrite removes keys on a provider whose update mechanism is a shallow merge (Gemini): the removal cannot be expressed, so honoring the rewrite would silently corrupt the tool call.
var ErrUnsupportedDecision = errors.New("agenthooks: decision unsupported by provider")
ErrUnsupportedDecision is returned (under Policy.Unsupported == Strict) when a handler decision cannot be expressed on the invoking provider.
Functions ¶
func CompileMatcher ¶
func CompileMatcher(p Provider, m ToolMatcher) (expr string, ok bool)
CompileMatcher renders the matcher in the provider's dialect. ok is false when the dialect can't express it (e.g. Cursor has no matchers), in which case config generation matches broadly and the runner filters in-process.
func Main ¶
func Main(r *Runner)
Main parses argv/stdin, runs the registered handlers, writes the response, and exits with the dialect-correct code. It also redirects the process's stdout so stray handler prints can't corrupt the wire (Gemini parses stderr as the decision when stdout is empty; Codex rejects unknown JSON).
When argv carries --async (rendered by install for telemetry events on providers without native async hooks, quirk #10), Main hands the payload to a detached copy of this process and exits immediately; the child runs the normal path without the flag. Detaching is process management, so it lives here rather than in the testable Run.
func SynthesizeToolID ¶
SynthesizeToolID derives a stable id for providers that omit one: hash(session|turn|tool|input) -> hook_synth_<16 hex>.
Types ¶
type AskFallback ¶
type AskFallback int
AskFallback selects the degradation target when Ask is unsupported.
const ( FallbackNoDecision AskFallback = iota FallbackDeny )
type CanonicalTool ¶
type CanonicalTool string
CanonicalTool classifies native tools into a small cross-provider taxonomy.
const ( ToolShell CanonicalTool = "shell" ToolFileRead CanonicalTool = "file.read" ToolFileWrite CanonicalTool = "file.write" ToolFileEdit CanonicalTool = "file.edit" ToolSearch CanonicalTool = "search" ToolFetch CanonicalTool = "fetch" ToolTask CanonicalTool = "task" ToolMCP CanonicalTool = "mcp" ToolOther CanonicalTool = "other" )
func CanonicalToolFor ¶
func CanonicalToolFor(name string) CanonicalTool
CanonicalToolFor classifies a native tool name. MCP names (any dialect) classify as ToolMCP.
type CapSet ¶
type CapSet map[Capability]bool
CapSet is the set of capabilities available on one provider+event pair.
func Capabilities ¶
Capabilities reports what a decision can express for the given provider/variant/event combination.
func (CapSet) Has ¶
func (s CapSet) Has(c Capability) bool
type Capability ¶
type Capability string
Capability names one thing a decision can express. The matrix below encodes which providers honor it on which events, so degradation is explicit and typed instead of silent.
const ( CapDeny Capability = "deny" CapAsk Capability = "ask" CapAllow Capability = "allow" CapUpdateInput Capability = "update-input" CapAddContext Capability = "add-context" CapReplaceOutput Capability = "replace-output" CapContinueAgent Capability = "continue-agent" CapStopAgent Capability = "stop-agent" CapSystemMessage Capability = "system-message" )
type CompactEvent ¶
type DegradeMode ¶
type DegradeMode int
DegradeMode governs unsupported decisions (e.g. Ask on Codex).
const ( // Degrade maps to the nearest supported intent (Ask->Deny or // Ask->NoDecision per AskFallback) and logs the downgrade. Degrade DegradeMode = iota // Strict treats an unsupported decision as a handler error, which then // follows Policy.Fail. Strict )
type DetectionConfidence ¶
type DetectionConfidence string
DetectionConfidence records how the invoking provider was identified.
const ( DetectionConfig DetectionConfidence = "config" // --provider flag from generated config DetectionEnv DetectionConfidence = "env" // environment variable sniffing DetectionShape DetectionConfidence = "shape" // payload shape sniffing )
type Event ¶
type Event struct {
Provider Provider
Variant Variant
NativeName string // "PreToolUse", "beforeShellExecution", "BeforeTool", ...
Kind EventKind // normalized; KindOther when unmapped
Time time.Time // library receive time; Gemini also supplies its own
Session SessionInfo
Agent *AgentInfo // non-nil inside a subagent context
DetectionConfidence DetectionConfidence
// Backfilled marks an event the provider never sent: some providers skip
// events in some modes (quirks #30, #31), and the runner synthesizes the
// miss on the next delivered event, best-effort. Backfilled events are
// reporting-only — Raw is nil, Can() reports no capabilities, and any
// decision returned by the handler is discarded.
Backfilled bool
// Raw is the verbatim provider payload. Never normalized, never trimmed.
Raw json.RawMessage
}
Event is the unified envelope. Raw is always the verbatim provider payload; normalization is a projection over it, never a replacement.
func EventOf ¶
EventOf returns the embedded envelope of any typed event returned by the decoder, or nil if the value is not an agenthooks event. Consumers that dispatch on the concrete types (rather than registering handlers) use it to reach the shared envelope fields.
func (*Event) Can ¶
func (e *Event) Can(c Capability) bool
Can reports whether the event's provider honors the capability. Backfilled events are reporting-only: the provider never sent them, so nothing can be gated or mutated.
type EventKind ¶
type EventKind string
EventKind is the unified, Claude-shaped event taxonomy. The set is open: any native event with no mapping is delivered as KindOther with the native name and raw payload intact.
const ( KindSessionStart EventKind = "session.start" KindSessionEnd EventKind = "session.end" KindPromptSubmitted EventKind = "prompt.submitted" KindToolPre EventKind = "tool.pre" // gate/rewrite before execution KindToolPost EventKind = "tool.post" KindToolError EventKind = "tool.error" KindPermission EventKind = "permission.request" KindStop EventKind = "agent.stop" // turn finished KindSubagentStart EventKind = "subagent.start" KindSubagentStop EventKind = "subagent.stop" KindCompactPre EventKind = "compact.pre" KindCompactPost EventKind = "compact.post" KindNotification EventKind = "notification" KindFileEdited EventKind = "file.edited" // post-hoc file-change reports KindModelRequest EventKind = "model.request" // gemini BeforeModel, opencode chat.params KindModelResponse EventKind = "model.response" KindOther EventKind = "other" // any unmapped native event )
type FileEditedEvent ¶
type MCPCall ¶
type MCPCall struct {
Server string // decoded from mcp__s__t / mcp_s_t / MCP:t + context; "" if undecodable
Tool string // tool name as the MCP server knows it
URL string // remote transport: payload-borne or config-resolved
Command string // stdio transport: command plus args, space-joined
// FromConfig marks URL/Command as resolved from the provider's config
// files rather than carried by the event payload. Config-resolved
// transport is best-effort: treat it as advisory, not authoritative.
FromConfig bool
}
MCPCall carries the decoded MCP tool identity plus the server's transport. Cursor and Gemini MCP events ship url/command in the hook payload; elsewhere the runner resolves them from provider config (quirk #25) and flags the result FromConfig.
func ParseMCPName ¶
ParseMCPName decodes the three MCP tool-name dialects: mcp__server__tool (Claude/Codex), mcp_server_tool (Gemini, best-effort since "_" is ambiguous), and MCP:tool (Cursor, server unknown). It returns nil when the name is not MCP-shaped.
type ModelEvent ¶
type ModelEvent struct {
Event
}
ModelEvent covers the experimental model.request/model.response kinds (Gemini BeforeModel/AfterModel, OpenCode chat.params). Observe-only in v1.
type NotificationEvent ¶
type Option ¶
type Option func(*Runner)
Option configures a Runner.
func WithDedupDir ¶
WithDedupDir relocates the on-disk cross-process state: the dedup markers used for Cursor's duplicate tool events (quirk #2), launch-context MCP inventory caches (quirks #26, #32), and the prompt-backfill markers (quirks #30, #31). Without this option, MCP inventories use os.UserCacheDir when available; other state uses os.TempDir.
func WithLogger ¶
WithLogger replaces the default logger (stderr, warn level).
func WithPolicyFunc ¶
func WithPolicyFunc(f PolicyFunc) Option
WithPolicyFunc resolves policy per event (ratchets, per-tool strictness, ...).
func WithoutBackfill ¶
func WithoutBackfill() Option
WithoutBackfill disables synthesizing reporting-only events for provider misses (the prompt.submitted backfill for Kimi/Cursor print modes, quirks #30 and #31).
func WithoutDedup ¶
func WithoutDedup() Option
WithoutDedup disables Cursor duplicate-event suppression.
func WithoutMCPListFallback ¶
func WithoutMCPListFallback() Option
WithoutMCPListFallback disables provider CLI inventory probes (`claude mcp list`, `codex mcp list --json`). Direct config-file resolution stays active; launch overrides that cannot be safely reproduced stay unattributed.
func WithoutMCPResolution ¶
func WithoutMCPResolution() Option
WithoutMCPResolution disables filling MCPCall.URL/Command when the payload carries no transport info — both the config-file fast path (quirk #25) and the `claude mcp list` fallback (quirk #26).
type PermissionEvent ¶
type Policy ¶
type Policy struct {
Fail FailMode
Unsupported DegradeMode
AskFallback AskFallback
// ContinuationCap caps ContinueWith loops; 0 means DefaultContinuationCap.
ContinuationCap int
// Timeout bounds handler execution. 0 derives a deadline from the
// --timeout flag in the generated config (90% of it) or defaultDeadline.
Timeout time.Duration
}
Policy declares how the runner behaves when a handler fails or asks for something the provider can't do. FailClosed is enforced with the provider's real mechanism per event; on events with no blocking mechanism it downgrades to logging (see Can(CapDeny)).
type PolicyFunc ¶
PolicyFunc resolves policy per event, enabling consumer patterns like a ratchet (fail-open until first success, then fail-closed).
type PromptDecision ¶
type PromptDecision struct {
// contains filtered or unexported fields
}
PromptDecision gates prompt.submitted events.
func AcceptPrompt ¶
func AcceptPrompt() PromptDecision
func BlockPrompt ¶
func BlockPrompt(reason string) PromptDecision
func (PromptDecision) StopAgent ¶
func (d PromptDecision) StopAgent(reason string) PromptDecision
func (PromptDecision) WithContext ¶
func (d PromptDecision) WithContext(s string) PromptDecision
func (PromptDecision) WithSystemMessage ¶
func (d PromptDecision) WithSystemMessage(s string) PromptDecision
type PromptEvent ¶
type Provider ¶
type Provider string
Provider identifies the coding agent that invoked the hook.
const ( ProviderClaudeCode Provider = "claude-code" // incl. cowork/desktop/web variants ProviderCursor Provider = "cursor" // incl. cursor-agent CLI, cloud agents ProviderCodex Provider = "codex" ProviderGemini Provider = "gemini" ProviderOpenCode Provider = "opencode" ProviderKimi Provider = "kimi-code" // Kimi Code CLI ("kimi" accepted as a flag alias) )
type Quirk ¶
type Quirk struct {
ID int
Provider Provider
Versions string // affected version range, free-form ("all", ">=2.0.64", ...)
Event EventKind
Capability Capability // "" when not capability-scoped
Behavior string
Mitigation string
Reference string // upstream issue/doc pointer
}
Quirk is one entry in the machine-readable registry of provider glue this library hides. The registry doubles as the conformance-test plan: every quirk gets a fixture. Docs markers (⚠) are generated from this table.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner holds registered handlers and policy. One handler per unified event kind; OnAny is additive (observe-only) and runs regardless. Typed handlers gate/mutate; OnAny never does.
func (*Runner) OnAny ¶
OnAny receives EVERY event, mapped or not, with the raw payload — the fidelity escape hatch. Observe-only: errors are logged, never gate.
func (*Runner) OnCompactPost ¶
func (r *Runner) OnCompactPost(fn func(context.Context, *CompactEvent) error)
func (*Runner) OnCompactPre ¶
func (r *Runner) OnCompactPre(fn func(context.Context, *CompactEvent) error)
func (*Runner) OnFileEdited ¶
func (r *Runner) OnFileEdited(fn func(context.Context, *FileEditedEvent) error)
func (*Runner) OnModelRequest ¶
func (r *Runner) OnModelRequest(fn func(context.Context, *ModelEvent) error)
func (*Runner) OnModelResponse ¶
func (r *Runner) OnModelResponse(fn func(context.Context, *ModelEvent) error)
func (*Runner) OnNotification ¶
func (r *Runner) OnNotification(fn func(context.Context, *NotificationEvent) error)
func (*Runner) OnOther ¶
OnOther receives events whose native name matches, typically ones with no unified mapping (Kind == KindOther). Observe-only.
func (*Runner) OnPermission ¶
func (r *Runner) OnPermission(fn func(context.Context, *PermissionEvent) (ToolPreDecision, error))
func (*Runner) OnPromptSubmitted ¶
func (r *Runner) OnPromptSubmitted(fn func(context.Context, *PromptEvent) (PromptDecision, error))
func (*Runner) OnSessionEnd ¶
func (r *Runner) OnSessionEnd(fn func(context.Context, *SessionEndEvent) error)
func (*Runner) OnSessionStart ¶
func (r *Runner) OnSessionStart(fn func(context.Context, *SessionStartEvent) (SessionStartDecision, error))
func (*Runner) OnSubagentStart ¶
func (r *Runner) OnSubagentStart(fn func(context.Context, *SubagentStartEvent) error)
func (*Runner) OnSubagentStop ¶
func (*Runner) OnToolError ¶
func (r *Runner) OnToolError(fn func(context.Context, *ToolPostEvent) (ToolPostDecision, error))
func (*Runner) OnToolPost ¶
func (r *Runner) OnToolPost(fn func(context.Context, *ToolPostEvent) (ToolPostDecision, error))
func (*Runner) OnToolPre ¶
func (r *Runner) OnToolPre(fn func(context.Context, *ToolPreEvent) (ToolPreDecision, error))
type SessionEndEvent ¶
type SessionInfo ¶
type SessionInfo struct {
ID string // claude/codex/gemini session_id; cursor conversation_id
TurnID string // codex turn_id, cursor generation_id, claude prompt_id ("" if absent)
CWD string
WorkspaceRoots []string // cursor multi-root; others: [CWD] or project dir
TranscriptPath string // "" if unavailable; format is provider-specific (see transcript pkg)
Model string // "" if not reported
PermissionMode string // claude/codex permission_mode; "" elsewhere
UserEmail string // cursor user_email; "" elsewhere
}
SessionInfo carries the normalized session identity fields.
type SessionStartDecision ¶
type SessionStartDecision struct {
// contains filtered or unexported fields
}
SessionStartDecision responds to session.start.
func ContinueSession ¶
func ContinueSession() SessionStartDecision
func (SessionStartDecision) WithContext ¶
func (d SessionStartDecision) WithContext(s string) SessionStartDecision
func (SessionStartDecision) WithSystemMessage ¶
func (d SessionStartDecision) WithSystemMessage(s string) SessionStartDecision
type SessionStartEvent ¶
type StopDecision ¶
type StopDecision struct {
// contains filtered or unexported fields
}
StopDecision responds to agent.stop / subagent.stop.
func ContinueWith ¶
func ContinueWith(instruction string) StopDecision
ContinueWith keeps the agent working: claude decision:block+reason, cursor followup_message, codex continuation prompt, gemini retry. The runner enforces Policy.ContinuationCap so consumers can't build infinite loops on providers without native caps.
func (StopDecision) WithSystemMessage ¶
func (d StopDecision) WithSystemMessage(s string) StopDecision
type StopEvent ¶
type StopEvent struct {
Event
// PreviouslyContinued surfaces stop_hook_active / loop_count uniformly.
PreviouslyContinued bool
// LoopCount is the provider-reported continuation count when available,
// or 1 when only a boolean guard (stop_hook_active) exists.
LoopCount int
// FinalMessage is the last assistant message of the turn when the provider
// includes it on the stop event (Claude/Codex last_assistant_message).
FinalMessage string
// Usage carries end-of-turn token/cost totals when the provider reports
// them on stop (Cursor), nil otherwise.
Usage *Usage
}
type SubagentStartEvent ¶
type SubagentStartEvent struct {
Event
}
type ToolCall ¶
type ToolCall struct {
ID string // native id, or synthesized (see Synthesized)
Synthesized bool // true when the provider omitted an id (Cursor MCP cases)
Name string // native tool name, verbatim
Canonical CanonicalTool // cross-provider classification
MCP *MCPCall // non-nil when the call targets an MCP tool
// Input is ALWAYS a JSON object (the library un-stringifies Cursor's
// string form and wraps non-object inputs as {"value": ...}).
Input json.RawMessage
// RawInput is the input exactly as the provider sent it (nil when the
// provider sent none and Input was constructed from other fields).
RawInput json.RawMessage
}
ToolCall is the normalized view of a tool invocation.
type ToolMatcher ¶
type ToolMatcher struct {
Names []string // exact native tool names
Canonical []CanonicalTool // canonical classes
MCP []string // "server/*" or "server/tool" globs
}
ToolMatcher is the unified tool matcher used both by config generation (compiled to each provider's matcher dialect where expressible) and by the runner's in-process filter (--filter flag) where not.
func ParseToolMatcher ¶
func ParseToolMatcher(s string) (ToolMatcher, error)
ParseToolMatcher parses the --filter flag format produced by Encode.
func (ToolMatcher) Encode ¶
func (m ToolMatcher) Encode() string
Encode serializes the matcher for the --filter flag baked into generated configs whose provider dialect can't express it.
func (ToolMatcher) IsEmpty ¶
func (m ToolMatcher) IsEmpty() bool
IsEmpty reports whether the matcher matches everything.
func (ToolMatcher) Matches ¶
func (m ToolMatcher) Matches(t ToolCall) bool
Matches reports whether the tool call satisfies the matcher. An empty matcher matches every tool.
type ToolPostDecision ¶
type ToolPostDecision struct {
// contains filtered or unexported fields
}
ToolPostDecision responds to tool.post / tool.error.
func FlagOutput ¶
func FlagOutput(reason string) ToolPostDecision
FlagOutput sends feedback about the tool output to the model.
func ReplaceOutput ¶
func ReplaceOutput(v any) ToolPostDecision
ReplaceOutput substitutes the tool output where supported: claude updatedToolOutput, cursor updated_mcp_tool_output (MCP only), gemini tool_output, opencode output mutation.
func (ToolPostDecision) StopAgent ¶
func (d ToolPostDecision) StopAgent(reason string) ToolPostDecision
func (ToolPostDecision) WithContext ¶
func (d ToolPostDecision) WithContext(s string) ToolPostDecision
func (ToolPostDecision) WithSystemMessage ¶
func (d ToolPostDecision) WithSystemMessage(s string) ToolPostDecision
type ToolPostEvent ¶
type ToolPostEvent struct {
Event
Tool ToolCall
Output json.RawMessage // provider's tool_response/output, verbatim
Failed bool // true on tool.error and error-carrying tool.post
Error string
// DurationMS is the tool execution time in milliseconds when the provider
// reports one (Cursor duration/duration_ms), nil otherwise.
DurationMS *float64
}
type ToolPreDecision ¶
type ToolPreDecision struct {
// contains filtered or unexported fields
}
ToolPreDecision gates tool.pre and permission.request events.
func Allow ¶
func Allow() ToolPreDecision
Allow skips the permission prompt where supported. It never loosens policy: provider-side deny rules still apply.
func AskUser ¶
func AskUser(reason string) ToolPreDecision
AskUser forces a confirmation prompt where the provider supports one. Behavior on providers without ask is governed by Policy.AskFallback.
func Deny ¶
func Deny(reason string) ToolPreDecision
Deny blocks the tool call with feedback for the model.
func NoDecision ¶
func NoDecision() ToolPreDecision
NoDecision defers to the provider's normal permission flow. It is NEVER a forced allow: the codecs emit each provider's correct "no opinion" form.
func (ToolPreDecision) StopAgent ¶
func (d ToolPreDecision) StopAgent(reason string) ToolPreDecision
StopAgent requests continue:false where supported.
func (ToolPreDecision) WithContext ¶
func (d ToolPreDecision) WithContext(s string) ToolPreDecision
WithContext injects context for the model where supported.
func (ToolPreDecision) WithSystemMessage ¶
func (d ToolPreDecision) WithSystemMessage(s string) ToolPreDecision
WithSystemMessage attaches a user-facing note where supported.
func (ToolPreDecision) WithUpdatedInput ¶
func (d ToolPreDecision) WithUpdatedInput(v any) ToolPreDecision
WithUpdatedInput rewrites the tool arguments before execution.
type ToolPreEvent ¶
type Usage ¶
type Usage struct {
InputTokens *int
OutputTokens *int
CacheReadTokens *int
CacheWriteTokens *int
Cost *float64
LoopCount *int
Status string
}
Usage carries the token and cost totals a provider reports at the end of a turn. Pointer fields distinguish "reported as zero" from "not reported".
Source Files
¶
- agenthooks.go
- backfill.go
- capability.go
- claudelaunch.go
- codec.go
- codec_claudecode.go
- codec_codex.go
- codec_cursor.go
- codec_gemini.go
- codec_kimi.go
- codec_opencode.go
- codexlaunch.go
- decision.go
- dedup.go
- detach.go
- detach_unix.go
- detect.go
- event.go
- log.go
- matcher.go
- mcplistcache.go
- mcplock_unix.go
- mcpresolve.go
- policy.go
- promptargv.go
- promptargv_linux.go
- quirks.go
- serve.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agenthookstest provides the golden fixture corpus and a fake-provider harness so hook binaries built on agenthooks can be integration-tested in CI without the actual agents (DESIGN.md §10).
|
Package agenthookstest provides the golden fixture corpus and a fake-provider harness so hook binaries built on agenthooks can be integration-tested in CI without the actual agents (DESIGN.md §10). |
|
Package e2e exercises agenthooks against real, locally installed coding agents: it renders hook configuration with the install package, points it at a recorder binary built from testdata/recorder, drives one headless agent turn, and asserts on the events that actually arrived and on decision enforcement (a deny must block the tool).
|
Package e2e exercises agenthooks against real, locally installed coding agents: it renders hook configuration with the install package, points it at a recorder binary built from testdata/recorder, drives one headless agent turn, and asserts on the events that actually arrived and on decision enforcement (a deny must block the tool). |
|
examples
|
|
|
secretguard
command
Command secretguard is an example agenthooks consumer: it scans every tool call's input for credential-shaped strings before execution.
|
Command secretguard is an example agenthooks consumer: it scans every tool call's input for credential-shaped strings before execution. |
|
Package install renders and installs provider hook configurations from a single Go Manifest: correct hooks.json / settings.json / config.toml / plugin scaffolding per provider, with the per-provider timing/async/ fail-mode workarounds baked in (DESIGN.md §7).
|
Package install renders and installs provider hook configurations from a single Go Manifest: correct hooks.json / settings.json / config.toml / plugin scaffolding per provider, with the per-provider timing/async/ fail-mode workarounds baked in (DESIGN.md §7). |
|
internal
|
|
|
jsonx
Package jsonx provides JSON decoding with unknown-field capture.
|
Package jsonx provides JSON decoding with unknown-field capture. |
|
transcriptio
Package transcriptio is the transcript-parsing core shared by the public transcript package and the runner's prompt backfill (the root package cannot import transcript without a cycle).
|
Package transcriptio is the transcript-parsing core shared by the public transcript package and the runner's prompt backfill (the root package cannot import transcript without a cycle). |
|
provider
|
|
|
claudecode
Package claudecode provides complete typed views over native Claude Code hook payloads.
|
Package claudecode provides complete typed views over native Claude Code hook payloads. |
|
codex
Package codex provides typed views over native Codex hook payloads.
|
Package codex provides typed views over native Codex hook payloads. |
|
cursor
Package cursor provides typed views over native Cursor hook payloads (IDE, cursor-agent CLI, and cloud agents).
|
Package cursor provides typed views over native Cursor hook payloads (IDE, cursor-agent CLI, and cloud agents). |
|
gemini
Package gemini provides typed views over native Gemini CLI hook payloads.
|
Package gemini provides typed views over native Gemini CLI hook payloads. |
|
kimicode
Package kimicode provides complete typed views over native Kimi Code CLI hook payloads.
|
Package kimicode provides complete typed views over native Kimi Code CLI hook payloads. |
|
opencode
Package opencode provides typed views over the NDJSON frames the generated agenthooks OpenCode shim plugin proxies from OpenCode's in-process plugin hooks (DESIGN.md §8).
|
Package opencode provides typed views over the NDJSON frames the generated agenthooks OpenCode shim plugin proxies from OpenCode's in-process plugin hooks (DESIGN.md §8). |
|
Package transcript provides best-effort JSONL readers for provider transcript files (Event.Session.TranscriptPath).
|
Package transcript provides best-effort JSONL readers for provider transcript files (Event.Session.TranscriptPath). |