hooks

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package hooks serves the Claude Code SessionStart and UserPromptSubmit hook endpoints and installs/removes their entries in a settings.json. Both handlers authenticate the same static bearer key as MCP, and both fail open: any internal error yields a 200 with empty additionalContext so a broken briefing can never block an agent. Only a bad key returns non-2xx (401).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CommandHookEndpoints

func CommandHookEndpoints() map[string]string

CommandHookEndpoints returns the `seam hook <arg>` events the installer wires as command hooks across every client profile, each mapped to the endpoint that hook must forward to. It is a union: the Codex profile adds user-prompt-submit (a command hook there, http for CC) and stop, so the CLI pin covers both clients. An event that both profiles wire as a command hook shares one endpoint.

It exists for the seam CLI's test. The CLI keeps its own copy of this mapping -- it cannot import this package without dragging the store, the retriever, and SQLite into a binary whose job is one HTTP POST -- and a hook fails open by contract, so drift between the two copies is a silent no-op rather than an error: install-hooks would write a command line the CLI rejects, or forward to a route that is not there, and the only symptom would be a briefing that stopped arriving.

func InstalledEvents

func InstalledEvents(client Client) []string

InstalledEvents is the set of hook events Seamless installs for a client, in install order. A caller (doctor) compares InstalledStatus against len(InstalledEvents) for the same client.

func InstalledStatus

func InstalledStatus(client Client, path, baseURL string) ([]string, error)

InstalledStatus reports which of a client's Seamless-managed hook events are present in the settings/hooks file at path, using the same ownership test as Install: the managed marker, or an unmarked entry that targets the hook's URL under baseURL or runs `... hook <event>` via the seam CLI. The marker alone cannot be trusted: Claude Code re-serializes settings.json through its own schema when the owner edits config or permissions, dropping the seamless_managed key while keeping the functional entries -- those still-firing hooks must count as installed. A missing or empty file yields an empty slice and no error. The result is a subset of InstalledEvents(client), in install order.

Types

type Client added in v0.3.3

type Client string

Client identifies the agent CLI a hook request came from. It selects the ambient session-name prefix (cc/ vs cx/) so a Claude Code agent and a Codex agent working the same machine get distinct, self-describing session names. The client rides on the hook payload; the --client plumbing that populates it from the request is a separate concern -- everything here only needs a Client value, and normalizeClient turns an absent or unknown one into ClientClaudeCode so any path not yet taught the discriminator keeps its Claude Code behavior.

const (
	// ClientClaudeCode is the default: Claude Code sends no discriminator, so an
	// empty client resolves here and existing cc/ behavior is unchanged.
	ClientClaudeCode Client = "claude-code"
	// ClientCodex is the Codex CLI, whose ambient sessions are named cx/.
	ClientCodex Client = "codex"
)

type Config

type Config struct {
	DB            *sql.DB
	Retrieve      *retrieve.Service
	Events        *events.Recorder
	Files         *files.Manager
	APIKey        string
	MaxEventChars int
	PlanCapture   config.PlanCapture
	PlansDir      string
	Logger        *slog.Logger
}

Config carries the Handler's dependencies. DB backs ambient sessions and the session-end harvest; Events may be nil (injection telemetry is then skipped); Files may be nil (plan/subagent capture is then skipped). MaxEventChars caps captured prompt/findings text (0 = unlimited); injected content is always stored in full (it is already bounded by the briefing/recall budgets upstream). PlansDir is where Claude Code writes plan-mode files; empty defaults to ~/.claude/plans (tests override it).

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler serves the hook endpoints.

func NewHandler

func NewHandler(cfg Config) *Handler

NewHandler builds a hook Handler from cfg.

func (*Handler) Register

func (h *Handler) Register(mux *http.ServeMux)

Register mounts the hook routes on mux at their full /api/hooks/* paths.

type InstallOptions

type InstallOptions struct {
	Client       Client // agent client profile; "" (zero value) => Claude Code
	SettingsPath string // target file: CC settings.json or Codex hooks.json (created if absent)
	BaseURL      string // e.g. http://127.0.0.1:8081
	APIKey       string // static bearer key (written into the CC http hook header; Codex command hooks carry none)
	SeamBin      string // path to the seam CLI for command hooks; "" => "seam" (PATH)
	ConfigPath   string // abs seamless.yaml passed to command hooks as `--config` so they resolve config from any cwd; "" omits it
}

InstallOptions configures an install.

type InstallResult

type InstallResult struct {
	Changed    bool
	BackupPath string   // "" when no backup was written
	Actions    []string // per-hook: "SessionStart: added|updated|unchanged"
}

InstallResult reports what an install did.

func Install

func Install(opts InstallOptions) (InstallResult, error)

Install merges the client's Seamless hook entries into the settings/hooks file at opts.SettingsPath, preserving unknown keys, replacing any existing Seamless-managed entries in place, and backing the file up once before the first change. It is idempotent: an already-current file is left untouched.

Jump to

Keyboard shortcuts

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