Skip to content
Development
Hook

Hooks

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

From plugin
loop-kit
87 skills6 agents2 hooks

Claude Code

> /plugin marketplace add cbdreamer11/CB-loop-kit-claude-plugin
> /plugin install loop-kit@loop-kit

What fires, and when

SessionStart

Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.

  • sh "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"

PreToolUse

  • MatchesBashsh "${CLAUDE_PLUGIN_ROOT}/hooks/guard-publish.sh"
Read hooks/hooks.json

Where it lives

  • hooks/guard-publish.shRunsGitHub
    Read the script
    #!/bin/sh
    # PreToolUse hook (matcher: Bash) — the only two laws a hook can actually enforce.
    #
    # Blocks: pushing to the protected branch, and the commands this project declared
    # forbidden in .loop/VERIFY.md. Exit 2 blocks the call and shows stderr to the agent.
    #
    # What this canNOT do, and the README says so plainly: it cannot tell whether
    # something was really verified. That is not enforceable by any hook — only
    # deterministic facts are. Do not pretend otherwise.
    #
    # Bypassable by design (--safe-mode, disabled hooks, managed settings). This is a
    # guardrail against an honest mistake, not a security boundary.
    #
    # Known limitation, stated rather than hidden: matching is done on the payload text, so a
    # command that merely CONTAINS a forbidden string is blocked too — a test, a grep, or an
    # echo that quotes it. Distinguishing "runs a push" from "mentions a push" needs real
    # command parsing, which is not worth a fragile POSIX implementation. When it fires on a
    # false positive, rephrase the command or let the owner run it.
    
    ROOT=${CLAUDE_PROJECT_DIR:-.}
    VERIFY="$ROOT/.loop/VERIFY.md"
    PAYLOAD=$(cat)
    
    # Only enforce in a project that actually adopted the loop. Installing this plugin must
    # never change how git behaves in unrelated repositories — that is how a useful guard
    # becomes the reason someone uninstalls it. (Caught in real use: with no VERIFY.md the
    # guard fell back to protecting `main` everywhere and blocked a legitimate push in a
    # project that had never run setup.)
    #
    # Note the scope limit: the hook can only see the session's project directory. If you
    # work on repository A in a session rooted at repository B, A's rules are not the ones
    # being applied. Declared, not hidden.
    [ -f "$VERIFY" ] || exit 0
    
    # Protected branch, as declared in VERIFY.md. If the field is still the template blank,
    # protect the usual suspects — the project adopted the loop, it just has not filled it in.
    BRANCH=$(sed -n 's/^- Protected branch[^`]*`\([^`]*\)`.*/\1/p' "$VERIFY" 2>/dev/null | head -1)
    case "$BRANCH" in ''|_____*) BRANCH="main master" ;; esac
    
    case "$PAYLOAD" in
      *"git push"*)
        for b in $BRANCH; do
          case "$PAYLOAD" in
            *"$b"*)
              echo "Blocked: pushing to the protected branch '$b' is the owner's action, not the agent's." >&2
              echo "Commit on a working branch instead, and report that it is ready to publish." >&2
              exit 2
              ;;
          esac
        done
        # A bare 'git push' on the protected branch is the same thing.
        CUR=$(git -C "$ROOT" branch --show-current 2>/dev/null)
        for b in $BRANCH; do
          if [ "$CUR" = "$b" ]; then
            echo "Blocked: the current branch is the protected branch '$b'. Publishing is the owner's call." >&2
            exit 2
          fi
        done
        ;;
    esac
    
    # Commands this project declared forbidden, comma-separated inside backticks.
    #
    # Note for anyone editing this: do NOT loop with `... | while read`. A pipeline runs
    # in a subshell, so `exit 2` would only leave the subshell and the hook would print
    # "Blocked" while returning 0 — a guard that says it blocks and does not. (That bug
    # was in the first version of this file and was caught by actually running it.)
    if [ -f "$VERIFY" ]; then
      FORBIDDEN=$(sed -n 's/^- Forbidden commands[^`]*`\([^`]*\)`.*/\1/p' "$VERIFY" 2>/dev/null | tr ',' '\n')
      OLDIFS=$IFS
      IFS='
    '
      for cmd in $FORBIDDEN; do
        IFS=$OLDIFS
        cmd=$(printf '%s' "$cmd" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        case "$cmd" in
          ''|_____*) IFS='
    '; continue ;;
        esac
        case "$PAYLOAD" in
          *"$cmd"*)
            echo "Blocked: '$cmd' is listed as a forbidden command in .loop/VERIFY.md." >&2
            echo "If it genuinely needs to run, the owner runs it." >&2
            exit 2
            ;;
        esac
        IFS='
    '
      done
      IFS=$OLDIFS
    fi
    
    exit 0
    
  • hooks/session-start.shRunsGitHub
    Read the script
    #!/bin/sh
    # SessionStart hook — mechanizes step 0 ("locate yourself").
    #
    # Whatever this prints on stdout is added to the session's context before the first
    # token, so a session lands already oriented: what is next, what is parked, what
    # landed recently, and which files another session may be holding.
    #
    # Never fails the session: exits 0 no matter what. If it cannot find the loop, it
    # stays quiet rather than shouting at a project that does not use it.
    
    ROOT=${CLAUDE_PROJECT_DIR:-.}
    STATE="$ROOT/.loop/STATE.md"
    
    [ -f "$STATE" ] || exit 0
    
    # Placeholder lines from the template (they contain <angle brackets>) are filtered
    # out everywhere: an unfilled template line read as a real gap sends a session
    # chasing work that does not exist.
    NOISE='<[^>]*>'
    
    echo "## Where this project stands (from .loop/STATE.md)"
    echo
    sed -n '/^## Epic:/,/^### Slices/p' "$STATE" | grep -vE "$NOISE" | sed -n '1,15p'
    echo
    echo "### Open items, gaps and blocks"
    OPEN=$(grep -E '^(\[ \]|GAP|PARKED|BLOCKED)' "$STATE" | grep -vE "$NOISE" | sed -n '1,20p')
    [ -n "$OPEN" ] && echo "$OPEN" || echo "(none recorded)"
    echo
    CLOSED=$(grep -E '^\[x\]' "$STATE" | grep -vE "$NOISE" | sed -n '1,10p')
    if [ -n "$CLOSED" ]; then
      echo "### Already done — do not rebuild these"
      echo "$CLOSED"
      echo
    fi
    
    if [ -f "$ROOT/.loop/GOTCHAS.md" ]; then
      echo "### Traps this project has already sprung (see .loop/GOTCHAS.md)"
      grep -E '^## ' "$ROOT/.loop/GOTCHAS.md" | sed -n '1,10p'
      echo
    fi
    
    echo "### Recent commits"
    git -C "$ROOT" log --oneline -15 2>/dev/null
    echo
    
    HOT=$(git -C "$ROOT" status --porcelain 2>/dev/null | sed -n '1,15p')
    if [ -n "$HOT" ]; then
      echo "### Uncommitted files — may belong to a parallel session, do not touch what is not yours"
      echo "$HOT"
      echo
    fi
    
    echo "Before building anything: read .loop/VERIFY.md and follow the loop skill."
    echo "Anything already committed is not rebuilt. Confirm against the code, not against a document."
    
    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.

@cbdreamer11
Ships withloop-kit

A working method for building real software with coding agents, across many sessions.

87 skills6 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
8
Star history of cbdreamer11/CB-loop-kit-claude-plugin on GitHub, a chart by star-history.com

More plugins that ship hooks.

superpowers

superpowers

A development methodology for coding agents that makes the agent brainstorm before building, follow plans, run parallel work and finish branches in a disciplined way. You get fewer skipped steps and more consistent results from your coding assistant.

15 skills1 hook311k
@obra@obra
Install Plugin
ponytail

ponytail

A Claude Code plugin with skills for auditing a whole repo, reviewing a change, listing deferred shortcut comments, and showing measured savings and a quick command reference.

6 skills10 hooks188k
@dietrichgebert@dietrichgebert
Install Plugin
agent-skills

agent-skills

Walks a software change from a written spec through design, testing, code review, CI/CD, and release, applying the matching engineering skill at each stage. Useful for developers using Claude Code on full development workflows.

25 skills+3112k
@addyosmani@addyosmani
Install Plugin
planning-with-files

planning-with-files

Persistent file-based planning for AI coding agents on long-running, multi-step tasks. It keeps task_plan.md, findings.md and progress.md on disk, so work can resume after /clear or context compaction.

6 skills+228k
@othmanadi@othmanadi
Install Plugin
gsd-core

gsd-core

A spec-driven development workflow for Claude Code that guides a project through planning, phases, milestone audits and fixes. Includes skills to generate tests from UAT criteria and to audit completed work.

72 skills+311k
@open-gsd@open-gsd
Install Plugin
superpowers-zh

superpowers-zh

Chinese-language skill pack for Claude Code that adds workflow guidance for brainstorming, TDD, debugging, and code review, plus Chinese conventions for commits, documentation, and domestic Git platforms like Gitee and Coding.net.

21 skills1 hook8.5k
@jnmetacode@jnmetacode
Install Plugin
socraticode

socraticode

Gives Claude Code semantic search, dependency graph and architecture context over a codebase, so an AI assistant can explore and understand a project's code. Includes skills for indexing a project and managing its file watching.

2 skills+33.4k
@giancarloerra@giancarloerra
Install Plugin
cocoindex-code

cocoindex-code

A lightweight code search engine that uses AST-based semantic search with local embeddings and incremental indexing. It helps Claude Code users find code faster and more efficiently.

1 skill1 MCP+13k
@cocoindex-io@cocoindex-io
Install Plugin

Questions about the hooks in loop-kit.

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