hooks

package
v0.4.9 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 32 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 (401) or an unknown ?client= discriminator (400, an install bug rather than a runtime condition) returns non-2xx.

Index

Constants

This section is empty.

Variables

HookClients lists every accepted hook client discriminator. It is the canonical set behind request parsing, programmatic profile selection, error text, and the seam CLI's test-pinned copy.

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, error)

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 RecordedCommandPaths added in v0.3.7

func RecordedCommandPaths(clientName Client, settingsPath string) (seamBin, configPath string, ok bool)

RecordedCommandPaths returns the seam binary and config path recorded in the first seam-shaped command hook of the client's profile in settingsPath. Doctor uses it to judge an install against the paths it was installed with rather than against the running binary's siblings: `make doctor` from a repo checkout must not report a ~/.local/bin install stale. ok is false when the file cannot be read or no command hook runs a seam executable.

Types

type Client added in v0.3.3

type Client string

Client identifies the agent host 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.

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 shared local Codex app/CLI/IDE host, 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 bounded by the client-aware context policy 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.

type InstallStatus added in v0.3.7

type InstallStatus struct {
	Current []string
	Stale   []string
	Owned   []string
}

InstallStatus separates exact current definitions from stale definitions that Seamless owns or can confidently adopt. Owned is their union in profile order and is the set uninstall may remove; foreign definitions are omitted.

func InstalledStatus

func InstalledStatus(opts InstallOptions) (InstallStatus, error)

InstalledStatus classifies every event in a client's profile against the exact desired definition described by opts. An event is Current only when it has exactly one current definition and no stale owned duplicate. A missing or empty file yields an empty status and no error.

type UninstallOptions added in v0.3.5

type UninstallOptions struct {
	Client       Client // agent client profile; "" (zero value) => Claude Code
	SettingsPath string // target file: CC settings.json or Codex hooks.json
	BaseURL      string // e.g. http://127.0.0.1:8081 -- the http-url ownership arm needs it
}

UninstallOptions configures an uninstall.

type UninstallResult added in v0.3.5

type UninstallResult struct {
	Changed    bool
	BackupPath string   // "" when no backup was written
	Actions    []string // per-hook: "SessionStart: removed|absent"
}

UninstallResult reports what an uninstall did.

func Uninstall added in v0.3.5

func Uninstall(opts UninstallOptions) (UninstallResult, error)

Uninstall removes the client's Seamless hook entries from the settings/hooks file at opts.SettingsPath, the exact inverse of Install. It uses the shared definition classifier and removes current, marked-stale, or confidently legacy Seamless entries. It preserves every unknown key and every foreign entry -- including a v1 "seam_managed" hook at a different URL. An event array that empties is dropped, and the top-level "hooks" key is dropped if it empties (the file itself is never deleted, even if it becomes "{}"). It backs the file up once before the first change (reusing backupOnce, which is once-ever, so it never clobbers Install's original backup). It is idempotent: a file with nothing of ours -- or a missing file -- is left untouched with no error.

Jump to

Keyboard shortcuts

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