Skip to content
Automation
Hook

Hooks

What kraken runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
kraken
53 skills2 hooks

Claude Code

> /plugin marketplace add rafael-adcp/kraken
> /plugin install kraken@kraken

What fires, and when

SessionEnd

  • bash "${CLAUDE_PLUGIN_ROOT}/hooks/session-end-release.sh"

StopFailure

  • Matchesrate_limitbash "${CLAUDE_PLUGIN_ROOT}/hooks/stop-failure-release.sh"
Read hooks/hooks.json

In the plugin's words

How kraken describes its own hook set.

An OPTIMIZATION over lease expiry, never the recovery mechanism: under kraken-protocol/9 a claim is a lease that frees itself within one TTL (PROTOCOL.md §5), and these hooks just hand it back sooner. SessionEnd (graceful exit) auto-releases so the task requeues in seconds

Where it lives

  • hooks/lib-release-claims.shGitHub
    Read the script
    # lib-release-claims.sh — the shared claim-release loop behind the lifecycle
    # hooks (session-end-release.sh, stop-failure-release.sh) and the external
    # ambush loop (scripts/kraken-loop.sh, any harness). Source it, then call
    # `release_all_claims "<reason>" [worker]` or
    # `release_session_claims "<reason>" <session-id>`. Not a hook itself.
    #
    # Discovery: `kraken.py claim` writes $KRAKEN_STATE_DIR/claim-<worker>.json on a
    # won claim; deliver/escalate/release remove it. The release functions run
    # `kraken.py release` for every claim-*.json still present that the caller may
    # free: the ambush loop scopes by its worker, SessionEnd by the agent session
    # (Claude Code, Copilot CLI or Pi) the claim recorded (#173), StopFailure
    # (account-wide) frees them all.
    #
    # Best-effort: a failed release falls back to the lease expiring on its own
    # (PROTOCOL.md §5) — it never fails the caller. Everything here is an
    # OPTIMIZATION over that expiry, not a precondition for recovery.
    
    # CLAUDE_PLUGIN_ROOT is set when Claude Code runs a hook; the dirname fallback
    # keeps the scripts runnable standalone (tests).
    KRAKEN_HOOKS_ROOT="${CLAUDE_PLUGIN_ROOT:-"$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"}"
    KRAKEN_BIN="$KRAKEN_HOOKS_ROOT/skills/unleash/kraken.py"
    KRAKEN_STATE="${KRAKEN_STATE_DIR:-$HOME/.kraken}"
    
    # Read a top-level string field out of a claim JSON — jq if present, else a
    # portable grep/sed fallback (the conformance suite keeps jq optional).
    kraken_json_field() { # $1 = file, $2 = field
      if command -v jq >/dev/null 2>&1; then
        jq -r --arg k "$2" '.[$k] // empty' "$1" 2>/dev/null
      else
        sed -n 's/.*"'"$2"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$1" | head -1
      fi
    }
    
    # release_claims_where REASON FIELD VALUE — run `kraken.py release` for every
    # open claim on this machine, or, with FIELD, only the claims whose recorded
    # FIELD equals VALUE. An empty VALUE for a named FIELD matches nothing: an owner
    # we cannot name is an owner we cannot prove, and the lease TTL covers it.
    # The released marker lands before in-progress drops and the claim ref last —
    # the ordering that frees the lock honestly (PROTOCOL.md §9).
    release_claims_where() {
      local reason="$1" field="${2:-}" value="${3:-}" f repo issue worker
      [ -z "$field" ] || [ -n "$value" ] || return 0
      [ -d "$KRAKEN_STATE" ] || return 0
      shopt -s nullglob 2>/dev/null || true
      for f in "$KRAKEN_STATE"/claim-*.json; do
        [ -f "$f" ] || continue
        repo="$(kraken_json_field "$f" repo)"
        issue="$(kraken_json_field "$f" issue)"
        worker="$(kraken_json_field "$f" worker)"
        # Skip a malformed/empty file we cannot act on, never guess.
        [ -n "$repo" ] && [ -n "$issue" ] && [ -n "$worker" ] || continue
        [ -z "$field" ] || [ "$(kraken_json_field "$f" "$field")" = "$value" ] || continue
        python3 "$KRAKEN_BIN" release "$repo" "$issue" "$worker" "$reason" >/dev/null 2>&1 || true
      done
      return 0
    }
    
    # release_all_claims REASON [WORKER] — every open claim on this machine, or,
    # with WORKER, only that worker's own claim (a loop that knows its identity must
    # never free a co-located worker's live claim).
    release_all_claims() {
      if [ -n "${2:-}" ]; then
        release_claims_where "$1" worker "$2"
      else
        release_claims_where "$1"
      fi
    }
    
    # release_session_claims REASON SESSION_ID — only the claims made inside that
    # agent session (`kraken.py claim` records CLAUDE_CODE_SESSION_ID, under
    # Copilot CLI COPILOT_AGENT_SESSION_ID, under Pi PI_SESSION_ID, as "session"). SessionEnd fires for
    # every session on the host, so this is the only scope that cannot free another
    # session's live claim (#173).
    release_session_claims() {
      release_claims_where "$1" session "${2:-}"
    }
    
    # release_detached FUNCTION ARG... — run one of the release functions above in
    # its own session, detached from the hook, and return at once. A harness bounds
    # how long a hook may run and kills it at the deadline: Claude Code gives
    # SessionEnd hooks 1.5s in total (a plugin's own "timeout" does not extend it,
    # verified on 2.1.283) and, under `claude -p`, kills a StopFailure hook as the
    # process exits — while a release against the real API makes ~9 calls and takes
    # seconds. Killed mid-release, a task is left half-freed (the released marker
    # posted, in-progress and the lock still held) until the TTL. Detached, the
    # release outlives the hook and whatever the harness does to it.
    # KRAKEN_HOOK_WAIT=1 waits for it instead (the conformance suite, which asserts
    # on the queue as soon as the hook returns).
    release_detached() {
      python3 -c '
    import os, sys
    lib, argv, wait = sys.argv[1], sys.argv[2:], os.environ.get("KRAKEN_HOOK_WAIT") == "1"
    ready_r, ready_w = os.pipe()
    pid = os.fork()
    if pid == 0:
        # A new session, out of the hook'"'"'s process group, BEFORE the hook may
        # return: a harness that kills the group right after must miss us.
        try:
            os.setsid()
        except OSError:
            pass
        os.write(ready_w, b"1")
        null = os.open(os.devnull, os.O_RDWR)
        for fd in (0, 1, 2):  # never hold the hook'"'"'s pipes: a harness waits on them
            os.dup2(null, fd)
        os.execvp("bash", ["bash", "-c", ". \"$0\"; \"$@\"", lib, *argv])
    os.close(ready_w)
    os.read(ready_r, 1)
    if wait:
        os.waitpid(pid, 0)
    ' "$KRAKEN_HOOKS_ROOT/hooks/lib-release-claims.sh" "$@" </dev/null >/dev/null 2>&1
      return 0
    }
    
    # hook_event_session_id — the `session_id` of the hook event JSON on stdin, or
    # empty when stdin is not a JSON object naming one.
    hook_event_session_id() {
      python3 -c '
    import json, sys
    try:
        event = json.load(sys.stdin)
    except ValueError:
        event = None
    sid = event.get("session_id") if isinstance(event, dict) else None
    print(sid if isinstance(sid, str) else "")
    ' 2>/dev/null
    }
    
  • hooks/session-end-release.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # session-end-release.sh — the bundled SessionEnd hook (hooks.json).
    #
    # When a worker's session ends *gracefully* (terminal closed, /exit) while it
    # still holds a lease, this releases it via the shared loop in
    # lib-release-claims.sh, so the task returns to the queue in seconds instead of
    # at the end of the lease TTL.
    #
    # This is an OPTIMIZATION, never the recovery mechanism: under kraken-protocol/9 the
    # claim is a lease that expires on its own (PROTOCOL.md §5), so a session that
    # fires no hook at all — a hard kill, a crash, a harness with no hook events —
    # still frees its task within one TTL. Releasing early is simply more polite to
    # the next worker. Graceful end only: a usage limit does NOT end the session, so
    # SessionEnd never fires there — that path is the StopFailure hook's
    # (stop-failure-release.sh). See the #60 FAQ in README.md.
    #
    # Three harnesses run it: Claude Code; GitHub Copilot CLI, which loads this
    # plugin's hooks.json and fires SessionEnd with the same Claude-shaped payload
    # (verified on Copilot CLI 1.0.88); and Pi, whose optional kraken extension
    # (extensions/pi/kraken.ts) pipes that payload in on session_shutdown.
    #
    # Scope: ONLY the ending session's own claims (#173). SessionEnd fires for
    # every agent session on the host — a 30-second `claude -p` or `copilot -p`
    # opened to read a file included — so the hook matches the event's
    # `session_id` against the session `kraken.py claim` recorded. A claim naming
    # no session (a bare shell) or an event naming none releases nothing: the lease
    # TTL, and the ambush loop's own release-on-exit, cover those.
    #
    # Detached: the release runs in its own session and the hook returns at once.
    # Claude Code gives SessionEnd hooks 1.5s in total — a plugin cannot raise it —
    # and a release against the real API takes longer; killed at the deadline, the
    # task was left half-released (see release_detached).
    #
    # Best-effort: ALWAYS exits 0 (a failed release just waits out the TTL).
    set -u
    # A harness exiting signals its still-running hooks (Claude Code under `claude
    # -p` does so within milliseconds): ignore that, so the hook lives long enough
    # to hand the release off.
    trap '' HUP INT TERM
    
    . "$(dirname "$0")/lib-release-claims.sh"
    
    session="$(hook_event_session_id)"
    [ -z "$session" ] || release_detached release_session_claims "session ended" "$session"
    
    exit 0
    
  • hooks/stop-failure-release.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # stop-failure-release.sh — the bundled StopFailure hook (hooks.json, matcher
    # `rate_limit`).
    #
    # A usage limit kills the turn but not the session, so SessionEnd never fires and
    # a held lease would squat on in-progress until its TTL runs out. StopFailure is
    # the one event that DOES fire there, so this hook releases every open claim on
    # this machine (reason "usage limit"): the task requeues in seconds, and the next
    # drain — a fresh `kraken-loop.sh` pass once the limit window resets — re-claims
    # it and continues on the existing branch.
    #
    # Matcher is `rate_limit` only: a usage limit is account-wide, so any session's
    # rate_limit failure implies the worker sessions here are limited too — releasing
    # their claims is correct. Per-session error types could fire on a healthy worker
    # and must not release its live claim, so they are deliberately not matched.
    #
    # Best-effort: ALWAYS exits 0. gh still works at limit time, so the release
    # normally lands; if not, the lease expiry is the backstop — this hook makes
    # recovery immediate, it is not what makes it happen. The StopFailure JSON on
    # stdin is unused — the matcher already scoped the error type.
    set -u
    # Under `claude -p` the process exits on the failed turn and signals its hooks
    # ~15ms after starting them (verified on Claude Code 2.1.283): ignore that, so
    # the hook lives long enough to hand the release off.
    trap '' HUP INT TERM
    
    . "$(dirname "$0")/lib-release-claims.sh"
    
    # Detached, like SessionEnd's: under `claude -p` the process exits on the failed
    # turn and would kill a release still in flight (see release_detached).
    release_detached release_all_claims "usage limit"
    
    exit 0
    

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

@rafael-adcp
Ships withkraken

You set the targets; the tentacles devour them. Unleash the Kraken. One head, many tentacles — a task queue built on GitHub Issues where named agent workers (Claude Code, GitHub Copilot CLI, Pi, or any tool that follows the protocol) claim tasks, execute

Stars on GitHub

Momentum

stars in total
5
Star history of rafael-adcp/kraken on GitHub, a chart by star-history.com

More plugins that ship hooks.

n8n-mcp-skills

n8n-mcp-skills

Expert Claude Code skills for building flawless n8n workflows using the n8n-mcp MCP server.

15 skills3 hooks6.6k
@czlonkowski@czlonkowski
Install Plugin
claude-scholar

claude-scholar

A research assistant plugin for Claude Code that supports literature review, paper writing, citation verification, Zotero and Obsidian integration, and code review and debugging for academic and software work.

45 skills+36k
@galaxy-dawn@galaxy-dawn
Install Plugin
octo

octo

Runs multi-AI workflows in Claude Code that split research, requirements scoping, implementation and review across several external AI providers, such as Codex, Gemini-style CLIs, Copilot, Ollama and OpenRouter, to cover each model's blind spots.

71 skills+34.4k
@nyldn@nyldn
Install Plugin
ai-maestro

ai-maestro

Orchestrates multiple Claude Code agents across terminals and machines, giving them persistent memory and agent-to-agent messaging so you no longer have to relay messages between them by hand.

37 hooks1 MCP853
@23blocks-os@23blocks-os
Install Plugin
godotmaker

godotmaker

An autonomous pipeline that turns a text description into a playable Godot game, generating design docs, game assets such as backgrounds, characters, props, cards and effects, and running visual checks and playtests.

41 skills+2562
@randallliuxin@randallliuxin
Install Plugin
sutando

sutando

A personal AI assistant with voice, screen and meeting support that learns your patterns and can update its own code. It runs across several Macs and can talk with other people's assistants.

87 skills20 hooks410
@sonichi@sonichi
Install Plugin
nextboard-hardware-solution

nextboard-hardware-sol…

Helps hardware teams turn product requirements into a reviewable, buildable PCB design plan. Covers embedded architecture, MCU or SoC selection, power tree, interfaces, sensors, connectivity, and BOM risk.

1 skill+2402
@leokemp223@leokemp223
Install Plugin
claude-prompts

claude-prompts

MCP server for reusable prompt templates, multi-step workflow chains, and quality gates. Compose agentic workflows with an operator syntax; export as native skills to Claude Code, Cursor, OpenCode, and Gemini CLI.

5 hooks1 MCP192
@minipuft@minipuft
Install Plugin

Questions about the hooks in kraken.

Which coding agents can use the hooks in kraken?
The hooks in kraken install in Claude Code and Cursor: each runs Claude Code hooks from .claude/settings.json, so copy the hooks there.