hooks

package
v0.3.5 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const ManagedCommand = "semantica"

ManagedCommand is the CLI command provider hooks should invoke by default. It intentionally uses the bare executable name so hook configs survive moves between install locations like Homebrew, curl installs, and local rebuilds.

Variables

View Source
var CWDKey = cwdKeyType{}

CWDKey carries the working directory from capture state into ReadFromOffset for providers whose transcripts don't embed a project path.

View Source
var CaptureTimestampKey = captureTimestampKeyType{}

CaptureTimestampKey carries the capture state's unix-ms timestamp into ReadFromOffset for turn-scoped enrichment.

View Source
var ErrNoCaptureState = errors.New("no capture state")

ErrNoCaptureState is returned when no capture state file exists for a session.

View Source
var HookEventTypeKey = hookEventTypeKeyType{}

HookEventTypeKey carries the current lifecycle event type into provider transcript preparation.

View Source
var HookTimestampKey = hookTimestampKeyType{}

HookTimestampKey carries the current hook timestamp into provider transcript preparation.

View Source
var ModelKey = modelKeyType{}

ModelKey carries the hook event's model name into ReadFromOffset.

Functions

func CWDFromContext

func CWDFromContext(ctx context.Context) string

CWDFromContext extracts the working directory from the context, or "" if absent.

func CaptureAndRoute

func CaptureAndRoute(ctx context.Context, provider HookProvider, event *Event, bh *broker.Handle, blobStore *blobs.Store) error

CaptureAndRoute reads a transcript delta, routes the resulting events, and advances the saved offset only after every repo write succeeds.

func DeleteCaptureState

func DeleteCaptureState(sessionID string) error

DeleteCaptureState removes a capture state file by session ID.

func DeleteCaptureStateByKey

func DeleteCaptureStateByKey(key string) error

DeleteCaptureStateByKey removes a capture state file by its key.

func Dispatch

func Dispatch(ctx context.Context, provider HookProvider, event *Event, bh *broker.Handle, blobStore *blobs.Store) error

Dispatch routes a normalized hook event.

func ExtractBinary added in v0.3.3

func ExtractBinary(command string) string

ExtractBinary returns the binary path from a hook command string. Handles both guarded ("if command -v X ...; then X capture ...; fi") and unguarded ("X capture ...") formats.

func GuardedCommand added in v0.3.3

func GuardedCommand(bin, args string) string

GuardedCommand wraps a capture command with a shell guard that silently no-ops when the binary is not on PATH. Ensures the hook never blocks the agent and never produces errors for teammates who don't have Semantica.

func HookTimestampFromContext

func HookTimestampFromContext(ctx context.Context) int64

HookTimestampFromContext extracts the current hook timestamp in unix-ms, or 0 if absent.

func ModelFromContext

func ModelFromContext(ctx context.Context) string

ModelFromContext extracts the model name from the context, or "" if absent.

func RegisterProvider

func RegisterProvider(p HookProvider)

RegisterProvider registers a hook provider by name. Called by provider packages in their init() functions.

func SaveCaptureState

func SaveCaptureState(state *CaptureState) error

SaveCaptureState writes a capture state file atomically.

Types

type CaptureState

type CaptureState struct {
	SessionID        string `json:"session_id"`
	StateKey         string `json:"state_key,omitempty"` // Override key; defaults to SessionID.
	Provider         string `json:"provider"`
	TranscriptRef    string `json:"transcript_ref"`
	TranscriptOffset int    `json:"transcript_offset"`
	Timestamp        int64  `json:"timestamp"`

	TurnID            string `json:"turn_id,omitempty"`
	PromptSubmittedAt int64  `json:"prompt_submitted_at,omitempty"`
	CWD               string `json:"cwd,omitempty"` // working directory from hook payload
}

CaptureState tracks the read position for an active transcript. Created when a turn starts, advanced on each capture, and removed after the turn finishes. Stored globally at ~/.semantica/capture/.

State file naming uses Key(): parent states are keyed by SessionID, subagent states by a derived identifier (e.g., the subagent transcript basename), so each transcript gets its own independent offset.

func LoadActiveCaptureStates

func LoadActiveCaptureStates() ([]*CaptureState, error)

LoadActiveCaptureStates scans all capture state files. Used by commit-time catch-up to flush all active sessions across all repos.

func LoadCaptureState

func LoadCaptureState(sessionID string) (*CaptureState, error)

LoadCaptureState reads a capture state file for the given session. Returns ErrNoCaptureState if the file does not exist.

func LoadCaptureStateByKey

func LoadCaptureStateByKey(key string) (*CaptureState, error)

LoadCaptureStateByKey reads a capture state file by its key. Returns ErrNoCaptureState if the file does not exist.

func (*CaptureState) Key

func (s *CaptureState) Key() string

Key returns the identifier used for the state file name. Subagent states set StateKey explicitly; parent states fall back to SessionID.

type DirectHookEmitter

type DirectHookEmitter interface {
	// BuildHookEvents constructs RawEvents from a hook Event's payload fields.
	// Called by the dispatcher for ToolStepCompleted, SubagentPromptSubmitted,
	// SubagentCompleted, and optionally PromptSubmitted. The provider stores
	// blobs via bs and returns fully populated RawEvents ready for routing and
	// writing.
	BuildHookEvents(ctx context.Context, event *Event, bs api.BlobPutter) ([]broker.RawEvent, error)
}

DirectHookEmitter is an optional interface for providers that can emit RawEvents directly from hook payloads without waiting for transcript replay. Used by providers that expose structured hook payloads for prompt and tool events such as Write, Edit, Bash, and Agent.

type Event

type Event struct {
	Type          EventType
	SessionID     string
	TranscriptRef string // path to transcript file
	Prompt        string // user prompt (PromptSubmitted only)
	Model         string // LLM model name
	Timestamp     int64  // unix ms, from hook payload or time.Now()
	ToolUseID     string // for subagent events and tool steps
	SubagentID    string
	Metadata      map[string]string

	// Step capture fields (ToolStepCompleted, SubagentPromptSubmitted).
	TurnID       string          // resolved from capture state or set by dispatcher
	CWD          string          // working directory from hook payload
	ToolName     string          // Write, Edit, Bash, Agent, etc.
	ToolInput    json.RawMessage // raw tool_input from hook payload
	ToolResponse json.RawMessage // raw tool_response from hook payload
}

Event is the provider-agnostic representation of an agent lifecycle event. Produced by HookProvider.ParseHookEvent from provider-specific stdin JSON.

type EventType

type EventType int

EventType represents a normalized agent lifecycle event.

const (
	PromptSubmitted EventType = iota
	AgentCompleted
	SessionOpened
	SessionClosed
	ContextCompacted
	SubagentSpawned
	SubagentCompleted
	ToolStepCompleted       // state-changing PostToolUse (Write, Edit, Bash)
	SubagentPromptSubmitted // PreToolUse[Agent] prompt event
)

func HookEventTypeFromContext

func HookEventTypeFromContext(ctx context.Context) (EventType, bool)

HookEventTypeFromContext extracts the current lifecycle event type from the context, or false if absent.

func (EventType) HookPhase

func (t EventType) HookPhase() string

HookPhase returns a short stable string for the event's lifecycle phase. Used by providers to disambiguate event IDs when the same tool_use_id appears in both a pre and post hook (e.g., PreToolUse[Agent] and PostToolUse[Agent] share a tool_use_id but are different events).

type HookProvider

type HookProvider interface {
	// Name returns the provider identifier (e.g., "claude-code").
	Name() string

	// DisplayName returns a human-friendly label (e.g., "Claude Code").
	DisplayName() string

	// IsAvailable reports whether the provider can be discovered on this
	// machine, either via an executable or provider-specific local state.
	IsAvailable() bool

	// InstallHooks writes hook configuration to the provider's config file
	// (e.g., <repoRoot>/.claude/settings.local.json). Returns the number
	// of hooks installed.
	InstallHooks(ctx context.Context, repoRoot string, binaryPath string) (int, error)

	// UninstallHooks removes Semantica hooks from the provider's repo-local
	// config file.
	UninstallHooks(ctx context.Context, repoRoot string) error

	// AreHooksInstalled checks if Semantica hooks are configured in the
	// given repo.
	AreHooksInstalled(ctx context.Context, repoRoot string) bool

	// HookBinary returns the executable name or path configured in the
	// provider's hook settings for the given repo.
	// Returns only the binary token, not the full command line with arguments.
	// Used by health checks to verify the binary is actually reachable via
	// exec.LookPath.
	HookBinary(ctx context.Context, repoRoot string) (string, error)

	// ParseHookEvent parses stdin JSON into a normalized Event.
	ParseHookEvent(ctx context.Context, hookName string, stdin io.Reader) (*Event, error)

	// TranscriptOffset returns the current position in the transcript
	// (line count for JSONL, message count for JSON).
	// Called during PromptSubmitted to capture the offset before the agent acts.
	TranscriptOffset(ctx context.Context, transcriptRef string) (int, error)

	// ReadFromOffset reads the transcript starting at the given offset and
	// returns parsed RawEvents. This is the enrichment step - extracting
	// file paths, token usage, tool calls from the provider's format.
	ReadFromOffset(ctx context.Context, transcriptRef string, offset int, bs api.BlobPutter) ([]broker.RawEvent, int, error)
}

HookProvider is implemented by each agent provider for hook-based capture.

func GetProvider

func GetProvider(name string) HookProvider

GetProvider returns the registered provider for the given name, or nil.

func ListAvailableProviders

func ListAvailableProviders() []HookProvider

ListAvailableProviders returns registered providers whose agent is detected on the current machine, in canonical order.

func ListProviders

func ListProviders() []HookProvider

ListProviders returns all registered providers in canonical order.

type SubagentDiscoverer

type SubagentDiscoverer interface {
	// DiscoverSubagentTranscripts returns paths to all subagent transcript
	// files associated with the given parent transcript. The parent transcript
	// path is used to derive the subagents directory (provider-specific).
	DiscoverSubagentTranscripts(ctx context.Context, parentTranscriptRef string) ([]string, error)

	// SubagentStateKey returns a stable key for the subagent's capture state
	// file, derived from the subagent transcript path. Must be unique per
	// subagent and safe for use as a filename component.
	SubagentStateKey(subagentTranscriptRef string) string
}

SubagentDiscoverer is an optional interface for providers that support subagent (child) transcripts stored separately from the parent transcript. When implemented, the SubagentCompleted handler scans for child transcripts and reads each one with its own capture state, ensuring subagent edits are attributed correctly.

type TranscriptPreparer

type TranscriptPreparer interface {
	// PrepareTranscript ensures the transcript file is complete and readable.
	// Called before ReadFromOffset. Must block until the file is ready or
	// a timeout is reached. Return nil on timeout so
	// capture proceeds with whatever data is available.
	PrepareTranscript(ctx context.Context, transcriptRef string) error
}

TranscriptPreparer is an optional interface for providers whose transcripts may not be fully flushed to disk when the hook fires. If implemented, PrepareTranscript is called before every ReadFromOffset.

Directories

Path Synopsis
Package builder provides shared scaffolding for the per-provider direct-emit hook handlers under internal/hooks/<provider>.
Package builder provides shared scaffolding for the per-provider direct-emit hook handlers under internal/hooks/<provider>.
Package testutil provides the shared golden-file harness for the direct-emit hook providers under internal/hooks/<provider>.
Package testutil provides the shared golden-file harness for the direct-emit hook providers under internal/hooks/<provider>.

Jump to

Keyboard shortcuts

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