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 0o755 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{}
ClaudeInstaller targets ~/.claude/settings.json.
Config shape (Claude Code, current as of kgraph reference):
{
"hooks": {
"SessionStart": [
{ "hooks": [ { "type": "command", "command": "/path/to/hook.sh" } ] }
]
}
}
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 (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{}
CodexInstaller targets ~/.codex/hooks.json (per kgraph reference).
Shape — kgraph wrote a flat map:
{"hooks":{"SessionStart":"<cmd>"}}
We keep the same shape for wire compatibility. Codex CLI's hook API is less stable than Claude Code's, so this is also flagged as a best-effort integration.
func (CodexInstaller) ConfigPath ¶
func (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{}
CopilotInstaller — GitHub Copilot CLI/VSCode integration.
NOTE — config path taken from kgraph's install.ts:
~/.config/github-copilot/hooks.json
kgraph's shape was a flat {"hooks":{"session-start":"<cmd>"}} map. That's what we mirror. GitHub Copilot does not officially document a SessionStart hook surface area as of this writing — treat this as a best-effort placeholder until Copilot publishes stable hook APIs.
func (CopilotInstaller) ConfigPath ¶
func (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{}
CursorInstaller writes to ~/.cursor/docsiq-hooks.json.
NOTE — schema guess flagged for verification: kgraph's install.ts does not wire Cursor into hook registration (only into MCP registration). Cursor has no canonical "SessionStart hook" primitive the way Claude Code does, so we stash our entry in a sibling docsiq-specific file and document that a user-level shell wrapper / workspace automation is expected to call hook.sh themselves. The JSON we write tracks WHICH hook is registered, so `status` and `uninstall` still work.
Shape:
{
"hooks": { "SessionStart": { "command": "/path/to/hook.sh" } }
}
func (CursorInstaller) ConfigPath ¶
func (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).