larik v0.3.0

Guide

Hooks

Hooks run at points in the agent lifecycle: shell commands, or prompts a model answers. The format matches Claude Code's, so existing hooks work. Matchers are case-insensitive, so Bash matches Larik's bash.

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "bash",
        "hooks": [
          { "type": "command", "command": "./scripts/guard.sh", "timeout": 10 }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "write|edit",
        "hooks": [
          { "type": "command", "command": "gofmt -l . >&2 && exit 2 || true" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "./scripts/require-tests.sh" }
        ]
      }
    ]
  }
}
Event Fires Can do
SessionStart first turn of each fresh context (matcher: startup, resume, clear) Add context. Stdout goes to the model.
UserPromptSubmit before a prompt is sent Block it, or add context (stdout)
PreToolUse before the permission check (matcher: tool name) allow (skips the prompt), deny, ask, or rewrite the input with updatedInput
PostToolUse after a tool runs Send feedback to the model
Stop / SubagentStop when the agent (or a subagent) would end its turn decision: "block" makes it continue with your reason (at most 5 times; stop_hook_active is set)
PreCompact, Notification, SessionEnd compaction, permission prompts, exit Observe only

How a hook's result is read:

  • Stdin is a JSON payload with session_id, transcript_path, cwd, hook_event_name, tool_name, tool_input, tool_response, and so on.
  • Exit 0: success. Stdout may be JSON: decision/reason, continue: false with stopReason (ends the turn), systemMessage, and hookSpecificOutput (permissionDecision, updatedInput, additionalContext).
  • Exit 2: blocks. Stderr is the reason, and it goes to the model.
  • Other exit codes: errors that don't block. They're shown to you.

Environment and timeouts:

  • LARIK_PROJECT_DIR is set, and so is CLAUDE_PROJECT_DIR for compatibility.
  • The default timeout is 60s.
  • Matching hooks run in parallel.

Precedence: a hook deny beats everything. Permission deny rules beat a hook allow.

Prompt hooks ask a model instead of running a command, for checks that need judgment:

json
{ "hooks": { "Stop": [ { "hooks": [
  { "type": "prompt", "prompt": "Did the agent run the tests and see them pass? Look at the transcript at transcript_path in: $ARGUMENTS" }
] } ] } }
  • $ARGUMENTS becomes the hook input as JSON; without it, the input is appended.
  • The model answers {"ok": true} or {"ok": false, "reason": "…"}. ok: false acts like exit code 2 for that event: a Stop hook makes the agent continue with the reason, a PreToolUse hook denies the call, a UserPromptSubmit hook blocks the prompt.
  • It runs on the hook's model (a provider/model or a routing role) if set, otherwise on your explore role, otherwise on the session's model. Its cost counts toward the session and its budget. The default timeout is 30s.
  • An answer that can't be read, a timeout or an error doesn't block; you see a message.

Trust: hooks in the shared .larik/settings.json don't run until you run /hooks approve. The approval is pinned to the hook set's content. Hooks in personal settings always run.

Instructions are loaded from these files:

  • AGENTS.md (or CLAUDE.md) in each directory from the repo root down to the working directory.
  • ~/.config/larik/AGENTS.md.

They are read at the start of each fresh context, so after editing one, /clear picks up the change.

Generated from README.md · section “Hooks”. Edit that file to change this page.