Documentation
¶
Overview ¶
Package hookinstaller registers docsiq's SessionStart hook with the various AI clients (Claude Code, Cursor, GitHub Copilot, Codex CLI).
Port of kgraph's hooks/install.ts. Key differences:
- POSIX-only: no hook.mjs, no hook.ps1, no Windows branches.
- Stdlib JSON with json.RawMessage for deep-merge safety.
- Atomic writes (temp-file + rename).
- Per-client "recognize our entry" is driven by the hook command path — so a user who moves ~/.docsiq/hooks/hook.sh can still uninstall the stale entry by rerunning against the old path.
Index ¶
Constants ¶
This section is empty.
Variables ¶
ErrConfigUnavailable is returned by ConfigPath when the client's config directory cannot be derived (no $HOME, etc).
var HookScript []byte
HookScript is the POSIX shell SessionStart hook. It ships embedded in the binary so `docsiq hooks install` never has to shell out to locate a source tree. See assets/hook.sh for the script itself.
Functions ¶
func ExtractHookScript ¶
ExtractHookScript writes the embedded hook.sh to dest with 0o700 perms. Creates the parent directory if missing. Overwrites existing files — callers that want to preserve a user-modified hook should check first.
Types ¶
type ClaudeInstaller ¶
type ClaudeInstaller struct {
// contains filtered or unexported fields
}
ClaudeInstaller targets ~/.claude/settings.json.
Config shape — schema source: https://docs.claude.com/en/docs/claude-code/hooks fetched 2026-04-17. Claude Code's SessionStart hook is a three-level nested structure: event -> matcher-group -> command-list:
{
"hooks": {
"SessionStart": [
{ "hooks": [ { "type": "command", "command": "/path/to/hook.sh" } ] }
]
}
}
Per docs, SessionStart supports optional matcher values ("startup", "resume", "clear", "compact"); omitting the matcher means the group activates on every occurrence. We omit it so our hook always fires.
We preserve all other top-level keys (mcpServers, permissions, etc) and only mutate hooks.SessionStart. Within SessionStart, we also preserve any entries that aren't ours.
func (ClaudeInstaller) ConfigPath ¶
func (c ClaudeInstaller) ConfigPath() (string, error)
func (ClaudeInstaller) Install ¶
func (c ClaudeInstaller) Install(hookPath string) error
func (ClaudeInstaller) Name ¶
func (ClaudeInstaller) Name() string
func (ClaudeInstaller) Status ¶
func (c ClaudeInstaller) Status() (bool, string)
func (ClaudeInstaller) Uninstall ¶
func (c ClaudeInstaller) Uninstall() error
type CodexInstaller ¶
type CodexInstaller struct {
// contains filtered or unexported fields
}
CodexInstaller targets ~/.codex/hooks.json.
UNVERIFIED — OpenAI Codex CLI does not publicly document a SessionStart hook API as of 2026-04-17. Doc sources checked:
- https://github.com/openai/codex (repo root)
- https://github.com/openai/codex/tree/main/docs
- https://github.com/openai/codex/blob/main/docs/config.md (documents TOML config at ~/.codex/config.toml; only a "Notify" post-turn notification hook is documented — no SessionStart)
Schema below mirrors kgraph's original guess (wrong config format AND wrong event name relative to real Codex, but preserved for wire compatibility with existing kgraph users until Codex publishes a stable hook API):
config path: ~/.codex/hooks.json
shape: {"hooks": {"SessionStart": "<cmd>"}}
func (CodexInstaller) ConfigPath ¶
func (c CodexInstaller) ConfigPath() (string, error)
func (CodexInstaller) Install ¶
func (c CodexInstaller) Install(hookPath string) error
func (CodexInstaller) Name ¶
func (CodexInstaller) Name() string
func (CodexInstaller) Status ¶
func (c CodexInstaller) Status() (bool, string)
func (CodexInstaller) Uninstall ¶
func (c CodexInstaller) Uninstall() error
type CopilotInstaller ¶
type CopilotInstaller struct {
// contains filtered or unexported fields
}
CopilotInstaller — GitHub Copilot CLI integration.
UNVERIFIED — GitHub Copilot CLI does not publicly document a SessionStart hook API as of 2026-04-17. Doc sources checked:
- https://docs.github.com/en/copilot/how-tos/use-copilot-agents/use-copilot-in-the-cli (404)
- https://docs.github.com/en/copilot/github-copilot-in-the-cli/about-github-copilot-in-the-cli (general responsible-use docs, no hook schema)
- https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-in-the-command-line (notes the legacy `gh copilot` extension was retired and replaced by the new Copilot CLI; no hook schema is published)
Schema below mirrors kgraph's original guess:
config path: ~/.config/github-copilot/hooks.json
shape: {"hooks": {"session-start": "<cmd>"}}
func (CopilotInstaller) ConfigPath ¶
func (c CopilotInstaller) ConfigPath() (string, error)
func (CopilotInstaller) Install ¶
func (c CopilotInstaller) Install(hookPath string) error
func (CopilotInstaller) Name ¶
func (CopilotInstaller) Name() string
func (CopilotInstaller) Status ¶
func (c CopilotInstaller) Status() (bool, string)
func (CopilotInstaller) Uninstall ¶
func (c CopilotInstaller) Uninstall() error
type CursorInstaller ¶
type CursorInstaller struct {
// contains filtered or unexported fields
}
CursorInstaller writes to ~/.cursor/docsiq-hooks.json.
UNVERIFIED — Cursor does not publicly document a SessionStart hook API as of 2026-04-17. Attempted doc sources returned empty / 404:
- https://docs.cursor.com/en/agent/hooks
- https://cursor.com/docs/agent/hooks
- https://docs.cursor.com/advanced/hooks
Cursor's primary extensibility surface is MCP (not shell hooks) and kgraph's install.ts only wires Cursor into MCP registration. Schema below mirrors kgraph's original guess; we stash our entry in a sibling docsiq-specific file so we at least have a place to track `status` and `uninstall` without polluting ~/.cursor/settings.json.
Shape (unverified guess):
{
"hooks": { "SessionStart": { "command": "/path/to/hook.sh" } }
}
func (CursorInstaller) ConfigPath ¶
func (c CursorInstaller) ConfigPath() (string, error)
func (CursorInstaller) Install ¶
func (c CursorInstaller) Install(hookPath string) error
func (CursorInstaller) Name ¶
func (CursorInstaller) Name() string
func (CursorInstaller) Status ¶
func (c CursorInstaller) Status() (bool, string)
func (CursorInstaller) Uninstall ¶
func (c CursorInstaller) Uninstall() error
type Installer ¶
type Installer interface {
Name() string
ConfigPath() (string, error)
Install(hookPath string) error
Uninstall() error
Status() (installed bool, detail string)
}
Installer is the contract every per-client installer satisfies.
Name — short client identifier ("claude", "cursor", ...). ConfigPath — absolute path the installer will read/write. Returning
an error here means "config dir cannot be determined" (e.g. $HOME is unset); this is not a user-visible error unless Install/Status is actually called.
Install — writes the hook entry to the config, merging with any
pre-existing content. Idempotent.
Uninstall — removes ONLY our entry; leaves unrelated keys intact. Status — returns (installed, detail) where detail is a short
human-readable string (path, reason missing, etc).