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 ¶
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 ¶
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 ¶
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.
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.
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 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 same three-way ownership test (managed marker, hook URL under BaseURL, or a `seam hook <event>` command) via seamlessIndices, so it also removes entries whose seamless_managed marker Claude Code stripped on an unrelated edit. It preserves every unknown key and every foreign entry -- including a v1 "seam_managed" hook at a different URL, which matches none of the three arms. 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.