hook

package
v1.14.0 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package hook is the thin adapter between the Claude Code hook contract and Director's coordination core (§5.4, §15.7). ALL knowledge of the hook wire shape — the stdin JSON fields, the stdout control protocol, the exit-code semantics — is isolated here so a contract change is a one-file edit (§15.7). That includes the other harnesses' dialects: Codex and the OpenCode plugin speak the CC shapes verbatim, and Copilot differs in exactly one place (a flat additionalContext instead of CC's hookSpecificOutput wrapper — see copilotContextOutput), which is why the context writers take a flavor. The handlers (sessionstart/posttooluse/stop/sessionend) work against the typed Input/control helpers in this file and never touch raw JSON or os.Stdin.

The cardinal rule (§13 t5, §5.4): a hook must NEVER block a session on internal failure. Dispatch wraps every handler so a panic or an error is recovered, logged loudly to health/, and turned into exit 0 with no blocking output. The single deliberate non-allow is the Stop emit-guard's decision:block, and only on a confident detection.

Index

Constants

View Source
const (
	EventSessionStart = "sessionstart"
	EventPostToolUse  = "posttooluse"
	EventStop         = "stop"
	EventSessionEnd   = "sessionend"
)

Hook event names. These are the suffixes the hidden `director _hook <event>` verb takes and the names the shims pass. They mirror CC's hook_event_name values, lowercased for the CLI surface.

Variables

This section is empty.

Functions

func Dispatch

func Dispatch(event string, in io.Reader, out io.Writer, hub string) (code int)

Dispatch is THE entry point the hidden `director _hook <event>` verb calls. It routes by event name to the matching handler and wraps the whole thing fail-safe: a panic or an error in parsing or in any handler is recovered, logged loudly to health/, and yields exit 0 with NO blocking output. The only path that blocks is the Stop emit-guard, which writes its block from inside the handler before returning nil.

It always returns 0 in v1: §13 t5 requires that a broken hook never blocks a session, and the only "control" Director exerts (SessionStart injection, the Stop block) travels over stdout, not the exit code. Returning int (not void) keeps room for an intentional non-zero later without touching the CLI seam.

func DispatchStdin

func DispatchStdin(event, hub string) int

DispatchStdin is the convenience the CLI's `_hook` verb wires to: it runs Dispatch against the process's real stdin/stdout. Kept here so the os-level wiring stays inside the adapter (the CLI just calls this with the event name and resolved hub).

Types

type Input

type Input struct {
	// Common to every hook event.
	SessionID      string `json:"session_id"`
	TranscriptPath string `json:"transcript_path"`
	CWD            string `json:"cwd"`
	HookEventName  string `json:"hook_event_name"`

	// SessionStart: startup | resume | clear | compact.
	Source string `json:"source"`

	// Stop / SubagentStop: true while a prior Stop hook is still active. We must
	// never block when true, or we create an infinite stop→block→stop loop.
	StopHookActive bool `json:"stop_hook_active"`

	// PostToolUse: the name of the tool that just ran. v1 captures only tool_name —
	// it's all the nudge gate needs. CC also sends tool_input/tool_response, but we
	// leave them unmodeled (json.Unmarshal ignores unmapped fields) rather than carry
	// unused fields.
	ToolName string `json:"tool_name"`

	// PreCompact: manual | auto. Unused in v1 beyond presence.
	Trigger string `json:"trigger"`

	// SessionEnd only: clear | logout | prompt_input_exit | other. How the session
	// ended; carried for health-log attribution, never for a branch — every reason
	// reaps the row alike.
	Reason string `json:"reason"`

	// Agent is a Director extension, NOT a CC wire field: an adapter that
	// fabricates payloads (the OpenCode plugin) names itself here so flavor
	// detection doesn't depend on heuristics. CC and the Codex shims never set
	// it; absent means "detect from the payload" (see agentFlavor).
	Agent string `json:"agent"`
}

Input is the typed projection of CC's hook stdin JSON. Only the fields Director's handlers use are modeled; unknown fields are ignored by the JSON decoder, so a CC addition can't break parsing. This struct is the ONLY place that names CC's wire fields — change it here if the contract moves (§15.7).

Jump to

Keyboard shortcuts

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