Skip to content
Development
Hook

Hooks

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

From plugin
agy-delegate
202 skills2 agents5 commands1 hook

Claude Code

> /plugin marketplace add davdittrich/delegate-agy
> /plugin install agy-delegate@agy-delegate

What fires, and when

SubagentStart

  • ${CLAUDE_PLUGIN_ROOT}/hooks/agy-subagent-policy.sh
Read hooks/hooks.json

Where it lives

  • hooks/agy-hooks-lib.shGitHub
    Read the script
    # agy-hooks-lib.sh
    #
    # Sourced-only bash function library for agy delegation hooks.
    # This file MUST NOT be executed directly and MUST NOT produce any
    # top-level side effects (no output, no `set -e`/`set -u` leakage into
    # the sourcing shell, no unguarded double-source work).
    #
    # Usage: source "$(dirname "${BASH_SOURCE[0]}")/agy-hooks-lib.sh"
    
    # Include guard: sourcing this file twice must be a cheap no-op.
    if [ -n "${_AGY_HOOKS_LIB_SOURCED:-}" ]; then
      return 0
    fi
    _AGY_HOOKS_LIB_SOURCED=1
    
    # agy_hooks_enabled
    #   Returns 0 (true) iff AGY_HOOKS_ENABLED is one of: 1, true, on, yes
    #   (case-insensitive). Default (unset/anything else) is disabled.
    agy_hooks_enabled() {
      local v="${AGY_HOOKS_ENABLED:-}"
      v="$(printf '%s' "$v" | tr '[:upper:]' '[:lower:]')"
      case "$v" in
        1|true|on|yes)
          return 0
          ;;
        *)
          return 1
          ;;
      esac
    }
    
    # agy_hooks_agent_allowed <agent_type>
    #   Returns 0 iff <agent_type> is allowed per AGY_HOOKS_AGENT_TYPES (CSV).
    #   Entries are trimmed of leading/trailing whitespace before comparison.
    #   Matching is exact and case-sensitive:
    #     - A namespaced entry (contains ':') matches ONLY an exact full-string
    #       equal agent_type (no wildcarding).
    #     - A bare entry (no ':') matches an agent_type that is exactly equal
    #       to it, OR a namespaced agent_type whose suffix after the last ':'
    #       equals it (bare entries act as a cross-namespace suffix wildcard).
    agy_hooks_agent_allowed() {
      local agent_type="$1"
      local list="${AGY_HOOKS_AGENT_TYPES:-general-purpose,Explore,metaswarm:researcher-agent,metaswarm:coder-agent,metaswarm:code-review-agent}"
      local -a entries
      IFS=',' read -ra entries <<< "$list"
    
      local entry suffix
      for entry in "${entries[@]}"; do
        # trim leading whitespace
        entry="${entry#"${entry%%[![:space:]]*}"}"
        # trim trailing whitespace
        entry="${entry%"${entry##*[![:space:]]}"}"
    
        [ -z "$entry" ] && continue
    
        if [[ "$entry" == *:* ]]; then
          # Namespaced entry: exact full-string match only, no wildcarding.
          if [ "$entry" = "$agent_type" ]; then
            return 0
          fi
        else
          # Bare entry: exact match, or suffix-after-last-colon match.
          if [ "$entry" = "$agent_type" ]; then
            return 0
          fi
          suffix="${agent_type##*:}"
          if [ "$entry" = "$suffix" ]; then
            return 0
          fi
        fi
      done
    
      return 1
    }
    
    # agy_hooks_parse_field <json_string> <field>
    #   Echoes the top-level string value of <field> from <json_string>.
    #   Echoes an empty string if the field is missing, not a string, or the
    #   JSON is invalid. All parsing goes through python3's json module
    #   (never eval). The python process always exits 0.
    agy_hooks_parse_field() {
      local json_string="$1"
      local field="$2"
    
      printf '%s' "$json_string" | python3 -c '
    import sys, json
    
    data = sys.stdin.read()
    field = sys.argv[1]
    try:
        obj = json.loads(data)
        v = obj.get(field, "")
        print(v if isinstance(v, str) else "")
    except Exception:
        print("")
    ' "$field"
    }
    
    # agy_hooks_debug <reason>
    #   If AGY_HOOKS_DEBUG=1, writes exactly one timestamped line containing
    #   <reason> to $AGY_HOOKS_DEBUG_FILE (append) if set, else to stderr.
    #   Never includes prompt/prompt-id content (callers must not pass any).
    #   No-op if debug is off.
    agy_hooks_debug() {
      local reason="$1"
    
      [ "${AGY_HOOKS_DEBUG:-}" = "1" ] || return 0
    
      local ts
      ts="$(date '+%Y-%m-%dT%H:%M:%S%z')"
      local line="[${ts}] agy-hooks: ${reason}"
    
      if [ -n "${AGY_HOOKS_DEBUG_FILE:-}" ]; then
        printf '%s\n' "$line" >> "$AGY_HOOKS_DEBUG_FILE"
      else
        printf '%s\n' "$line" >&2
      fi
    }
    
  • hooks/agy-subagent-policy.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # hooks/agy-subagent-policy.sh
    #
    # SubagentStart hook. Reads a hook-event JSON payload on stdin describing the
    # subagent about to be spawned. When -- and ONLY when -- the hook is enabled,
    # the subagent's agent_type is allowlisted, AND `agy-bridge` is available on
    # PATH, this hook emits a fixed advisory as SubagentStart additionalContext:
    #
    #   {"hookSpecificOutput":{"hookEventName":"SubagentStart","additionalContext":"<advisory>"}}
    #
    # In every other case it is completely SILENT (no stdout) and exits 0. This
    # hook must never fail, never hang a subagent spawn, and never echo the
    # incoming prompt/payload back to stdout.
    
    set -uo pipefail
    
    _hook_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    # shellcheck source=./agy-hooks-lib.sh
    source "${_hook_dir}/agy-hooks-lib.sh"
    
    # Read the entire hook-event payload from stdin. Safe even if stdin is empty
    # or already closed -- never blocks. This must run before any guard so stdin
    # is always drained regardless of which branch exits.
    input="$(cat)"
    
    # Guard 1 (FIRST guard that can exit): the hook must be explicitly enabled.
    # This is pure bash (agy_hooks_enabled has no python3 dependency), so the
    # default-OFF path never forks an interpreter.
    if ! agy_hooks_enabled; then
      agy_hooks_debug "disabled -> skip"
      exit 0
    fi
    
    # Guard 2: python3 must actually be usable (not just present on PATH). A
    # present-but-broken python3 (e.g. invocation/import failure) is treated the
    # same as an absent one -- both are "no python3", never misreported as
    # malformed json by the later probe. Only reached when the hook is enabled,
    # so this never forks an interpreter on the default-OFF path. The probe body
    # must not read stdin.
    if ! python3 -c 'import json' >/dev/null 2>&1; then
      agy_hooks_debug "no python3 -> skip"
      exit 0
    fi
    
    # Guard 3: empty or whitespace-only stdin is its own fail-safe branch. A
    # missing payload is operationally distinct from a valid-but-not-allowlisted
    # agent, so it gets its own debug reason (see AGY_HOOKS_DEBUG allowlist
    # discovery UX).
    if [[ -z "${input//[[:space:]]/}" ]]; then
      agy_hooks_debug "empty stdin -> skip"
      exit 0
    fi
    
    # Guard 4: malformed / non-JSON payload is its own fail-safe branch. One
    # python3 validity probe (never eval); a broken payload must be
    # distinguishable from a not-allowlisted agent in the debug log.
    if ! printf '%s' "$input" | python3 -c 'import json,sys; json.loads(sys.stdin.read())' >/dev/null 2>&1; then
      agy_hooks_debug "malformed json -> skip"
      exit 0
    fi
    
    # Parse (never eval) the subagent's agent_type from the payload. Valid JSON
    # with an empty/missing agent_type yields an empty string, which legitimately
    # falls through to the not-allowlisted branch below.
    agent_type="$(agy_hooks_parse_field "$input" agent_type)"
    
    # Guard 5: the subagent's agent_type must be allowlisted.
    if ! agy_hooks_agent_allowed "$agent_type"; then
      agy_hooks_debug "not allowlisted: '${agent_type}' -> skip"
      exit 0
    fi
    
    # Guard 6: agy-bridge must actually be available on PATH.
    if ! command -v agy-bridge >/dev/null 2>&1; then
      agy_hooks_debug "agy-bridge not on PATH -> skip"
      exit 0
    fi
    
    # All guards passed: emit the fixed advisory as SubagentStart
    # additionalContext. The advisory is a fixed python constant -- it is NEVER
    # built via shell string interpolation, and the incoming prompt/payload is
    # never echoed.
    python3 <<'PYEOF'
    import json
    
    advisory = """For general web search, prefer your `WebSearch` tool. For grounded/source-cited search, extended-context reading, or a second opinion you can delegate to agy via the `agy-bridge` command — invoke it through `ctx_shell` (fall back to `Bash` only if `ctx_shell` is unavailable); run `agy-bridge --help`. The judgment stays with you: skip it for small or judgment-heavy tasks, and always verify agy's output."""
    
    output = {
        "hookSpecificOutput": {
            "hookEventName": "SubagentStart",
            "additionalContext": advisory,
        }
    }
    print(json.dumps(output))
    PYEOF
    
    agy_hooks_debug "fired"
    
    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.

@davdittrich
Ships withagy-delegate

Claude Code plugin that bridges to agy (Google Antigravity CLI) — adds Gemini, GPT-OSS, and grounded web search to Claude sessions

202 skills2 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
20
Star history of davdittrich/delegate-agy 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+3117k
@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 agy-delegate.

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