Documentation
¶
Overview ¶
Package hook runs user-configured shell-command hooks around the agent loop: PreToolUse / PostToolUse fire around each tool call, UserPromptSubmit before a turn, Stop after it. Hooks come from settings.json — a project (.ok/settings.json, only when the project is trusted) and a global (~/.ok/settings.json) file. A hook's exit code is its verdict: 0 = pass, 2 = block (only on the gating events), other = warn. The payload is delivered as JSON on stdin; output is captured (capped) and surfaced to the user. This package only loads, matches, and runs hooks; the agent and controller decide what a block means (see internal/agent, internal/control).
Index ¶
- Constants
- Variables
- func FormatOutcome(o Outcome) string
- func GlobalSettingsPath(homeDir string) string
- func IsBlocking(e Event) bool
- func IsTrusted(projectRoot, homeDir string) bool
- func MatchesTool(h ResolvedHook, toolName string) bool
- func ProjectDefinesHooks(projectRoot string) bool
- func ProjectSettingsPath(projectRoot string) string
- func Trust(projectRoot, homeDir string) error
- func TrustPath(homeDir string) string
- type Decision
- type Event
- type HookConfig
- type LoadOptions
- type Outcome
- type Payload
- type Report
- type ResolvedHook
- type Runner
- func (r *Runner) ConsumeRollback() (string, string, bool)
- func (r *Runner) Enabled() bool
- func (r *Runner) Hooks() []ResolvedHook
- func (r *Runner) PostToolUse(ctx context.Context, name string, args json.RawMessage, result string)
- func (r *Runner) PreToolUse(ctx context.Context, name string, args json.RawMessage) (block bool, message string)
- func (r *Runner) PromptSubmit(ctx context.Context, prompt string, turn int) (block bool, message string)
- func (r *Runner) Stop(ctx context.Context, lastAssistant string, turn int)
- type Scope
- type Settings
- type SpawnInput
- type SpawnResult
- type Spawner
Constants ¶
const ( SettingsDirname = ".ok" SettingsFilename = "settings.json" )
SettingsDirname / SettingsFilename locate a scope's settings.json.
const TrustFilename = "trust.json"
TrustFilename is the user-global trust store under ~/.ok.
Variables ¶
var Events = []Event{PreToolUse, PostToolUse, UserPromptSubmit, Stop}
Events is every event, in a stable order — drives loading and `/hooks`.
Functions ¶
func FormatOutcome ¶
FormatOutcome renders a non-pass outcome as a one-line human message.
func GlobalSettingsPath ¶
GlobalSettingsPath is ~/.ok/settings.json (homeDir overrides ~).
func IsBlocking ¶
IsBlocking reports whether a non-zero/exit-2 (or timed-out) hook on this event can block the loop. Only the gating events qualify.
func MatchesTool ¶
func MatchesTool(h ResolvedHook, toolName string) bool
MatchesTool reports whether a hook applies to toolName. The match field is an anchored regex; non-tool events always match. A malformed regex never fires (safer than firing on everything).
func ProjectDefinesHooks ¶
ProjectDefinesHooks reports whether a project's settings.json exists and declares at least one hook — regardless of trust. Frontends use this to decide whether to prompt the user to trust the project.
func ProjectSettingsPath ¶
ProjectSettingsPath is <root>/.ok/settings.json.
Types ¶
type HookConfig ¶
type HookConfig struct {
// Match is an anchored regex selecting tools (Pre/PostToolUse only); "" or
// "*" = every tool. Anchored: "file" won't match "read_file" — use ".*file".
Match string `json:"match,omitempty"`
// Command is the shell command to run (spawned through the platform shell).
Command string `json:"command"`
// Description is an optional human label surfaced in `/hooks`.
Description string `json:"description,omitempty"`
// Timeout overrides the per-event default, in milliseconds.
Timeout int `json:"timeout,omitempty"`
// Cwd overrides the working directory (defaults to the payload's cwd).
Cwd string `json:"cwd,omitempty"`
}
HookConfig is one hook as written in settings.json.
type LoadOptions ¶
LoadOptions configure Load. Project hooks load only when Trusted; global hooks always load.
type Outcome ¶
type Outcome struct {
Hook ResolvedHook
Decision Decision
ExitCode int // -1 when unknown (killed / spawn error)
Stdout string
Stderr string
TimedOut bool
Truncated bool
Duration time.Duration
}
Outcome records one hook invocation.
type Payload ¶
type Payload struct {
Event Event `json:"event"`
Cwd string `json:"cwd"`
ToolName string `json:"toolName,omitempty"`
ToolArgs json.RawMessage `json:"toolArgs,omitempty"`
ToolResult string `json:"toolResult,omitempty"`
Prompt string `json:"prompt,omitempty"`
LastAssistant string `json:"lastAssistantText,omitempty"`
Turn int `json:"turn,omitempty"`
}
Payload is the JSON envelope written to a hook's stdin.
type Report ¶
type Report struct {
Event Event
Outcomes []Outcome
Blocked bool // at least one outcome blocked (only meaningful on gating events)
}
Report aggregates the outcomes of running an event's hooks.
type ResolvedHook ¶
type ResolvedHook struct {
HookConfig
Event Event
Scope Scope
Source string // absolute path to the settings.json it came from
// contains filtered or unexported fields
}
ResolvedHook is a loaded hook with its origin baked in.
func Load ¶
func Load(opts LoadOptions) []ResolvedHook
Load resolves hooks: project first (only when trusted), then global; within a scope, settings.json array order. A malformed file yields no hooks (never an error — a typo shouldn't take down the CLI).
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner binds a set of resolved hooks to a session: a working directory, the spawner, and a notify callback that surfaces non-blocking hook messages to the user. It is the single object the agent (tool events) and the controller (prompt/stop events) fire hooks through, so neither has to know how hooks load or run. A nil *Runner is a valid no-op (no hooks configured).
func NewRunner ¶
func NewRunner(hooks []ResolvedHook, cwd string, spawner Spawner, notify func(string)) *Runner
NewRunner builds a Runner. spawner nil uses DefaultSpawner; notify nil drops non-blocking messages.
func (*Runner) ConsumeRollback ¶
ConsumeRollback reports whether the last tool was rolled back. Shell-hook Runners never roll back — only the DST compile/test hooks do.
func (*Runner) Hooks ¶
func (r *Runner) Hooks() []ResolvedHook
Hooks returns the resolved hooks (for `/hooks` listing).
func (*Runner) PostToolUse ¶
PostToolUse fires after a tool call. It can't block; non-pass outcomes are surfaced to the user via notify.
func (*Runner) PreToolUse ¶
func (r *Runner) PreToolUse(ctx context.Context, name string, args json.RawMessage) (block bool, message string)
PreToolUse fires before a tool call. block=true means the call must be refused; message is the reason (fed back to the model and shown to the user).
type Scope ¶
type Scope string
Scope records which settings.json a hook came from. Project hooks fire before global ones.
type Settings ¶
type Settings struct {
Hooks map[Event][]HookConfig `json:"hooks"`
}
Settings is the shape of a settings.json (only hooks for now).
type SpawnInput ¶
SpawnInput / SpawnResult / Spawner are the test seam around the real spawn.
type SpawnResult ¶
type SpawnResult struct {
ExitCode int
Stdout string
Stderr string
TimedOut bool
SpawnErr error
Truncated bool
}
func DefaultSpawner ¶
func DefaultSpawner(ctx context.Context, in SpawnInput) SpawnResult
DefaultSpawner runs the command through the platform shell with the payload on stdin, capping captured output and honoring both the per-hook timeout and the parent context's cancellation.
type Spawner ¶
type Spawner func(ctx context.Context, in SpawnInput) SpawnResult