Skip to content
Development
Hook

Hooks

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

From plugin
total-agent-memory
754 skills7 hooks1 MCP

Claude Code

> /plugin marketplace add vbcherepanov/total-agent-memory
> /plugin install total-agent-memory@vbcherepanov

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.

  • Matchesstartup|resume|clear|compact"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"

UserPromptSubmit

Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.

  • "${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.sh"

PreToolUse

  • MatchesEdit|Write|MultiEdit|NotebookEdit"${CLAUDE_PLUGIN_ROOT}/hooks/pre-edit.sh"
  • MatchesBash|Edit|Write|MultiEdit|NotebookEdit"${CLAUDE_PLUGIN_ROOT}/hooks/rule-reminder.sh"

PostToolUse

  • MatchesEdit|Write|MultiEdit|NotebookEdit|Bash"${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use.sh"

PostToolUseFailure

  • MatchesBash"${CLAUDE_PLUGIN_ROOT}/hooks/on-bash-error.sh"
  • MatchesEdit|Write|MultiEdit|NotebookEdit|Bash"${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use.sh"

Stop

  • "${CLAUDE_PLUGIN_ROOT}/hooks/on-stop.sh"

SessionEnd

  • "${CLAUDE_PLUGIN_ROOT}/hooks/session-end.sh"
Read hooks/hooks.json

Where it lives

  • hooks/auto-capture.ps1GitHub
  • hooks/auto-capture.shGitHub
  • hooks/codex-notify.ps1GitHub
  • hooks/codex-notify.shGitHub
  • hooks/inject-provenance.ps1GitHub
  • hooks/inject-provenance.shGitHub
  • hooks/memory-trigger.ps1GitHub
  • hooks/memory-trigger.shGitHub
  • hooks/on-bash-error.ps1GitHub
  • hooks/on-bash-error.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # Bash failure hook — v7.0 learn_error trigger
    #
    # Fires when a Bash call fails (PostToolUseFailure, or PostToolUse with a
    # non-zero exit code in the response) and stderr/error text is present.
    # Asks the model to call learn_error(...) via additionalContext;
    # N>=3 similar patterns auto-consolidate into a rule on the MCP side.
    #
    # Hook: PostToolUseFailure (matcher: "Bash"), PostToolUse (matcher: "Bash")
    # ===========================================
    
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    hook_set_event "PostToolUse"
    
    TOOL=$(hook_get '.tool_name')
    [ "$TOOL" != "Bash" ] && exit 0
    
    # Empty or zero -> no error, skip
    case "$HOOK_EXIT_CODE" in
        ""|0|null) exit 0 ;;
    esac
    
    # Skip user aborts
    [ "$HOOK_INTERRUPTED" = "1" ] && exit 0
    
    STDERR="${HOOK_ERROR_TEXT:0:500}"
    
    # Skip noise: user aborts, interactive prompts, benign warnings
    case "$STDERR" in
        *"permission denied by user"*|*"User denied"*|*"SIGINT"*) exit 0 ;;
    esac
    
    # Only react if there is an actionable signal
    [ -z "$STDERR" ] && exit 0
    
    hook_add_context "v7.0 learn_error trigger: bash exited $HOOK_EXIT_CODE. If the root cause is
    reproducible and fixable, call:
      learn_error(
          file=\"<path if relevant>\",
          error=\"<short stderr>\",
          root_cause=\"<what actually failed>\",
          fix=\"<what resolves it>\",
          pattern=\"<short slug, e.g. sqlite-locked-during-ddl>\"
      )
    Skip if this is user-aborted, interactive, or benign. After N>=3 same patterns
    it auto-consolidates into a rule — do not re-log if you just fixed it and the
    root cause is identical to an earlier call this turn."
    
    exit 0
    
  • hooks/on-stop.ps1GitHub
  • hooks/on-stop.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # On Stop Hook — Portable version
    #
    # Runs at the end of EVERY assistant turn, so it stays cheap and quiet:
    #
    # 1. Refreshes ONE recovery snapshot per session
    #    ($TAM_MEMORY_DIR/recovery/pending-<session>.md, at most once every
    #    STOP_SNAPSHOT_MIN_INTERVAL_SEC) in case the session dies without
    #    SessionEnd. SessionEnd overwrites it with the transcript excerpt.
    # 2. Once per session, when there were >=3 edits and no memory_save, shows
    #    the USER a systemMessage reminder. Stop hooks cannot add model context
    #    and must never block, so nothing model-directed is printed.
    # 3. Ambient turn end (src/ambient/turn_end.py): checks the turn's logged
    #    actions against the rules surfaced to the session (a broken
    #    prohibition is reported in the same systemMessage) and, when the turn
    #    changed something, auto-saves a short turn summary
    #    (TAM_RULE_COMPLIANCE / TAM_TURN_AUTOSAVE, both default true).
    # 4. Prunes nudge and ambient state older than 7 days.
    #
    # No desktop notification per turn.
    #
    # Hook: Stop (matcher: "")
    # ===========================================
    
    HOOK_PARSE_FIELDS=0
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    
    printf '%s' "$HOOK_INPUT" | hook_helper stop \
        --memory-dir "$CLAUDE_MEMORY_DIR" \
        --recovery-dir "$HOOK_RECOVERY_DIR" \
        --src "$HOOK_SRC_DIR"
    exit 0
    
  • hooks/post-tool-use.ps1GitHub
  • hooks/post-tool-use.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # PostToolUse Hook — observation capture + memory_save nudges
    #
    # Runs AFTER a tool call (PostToolUse) or a failed one (PostToolUseFailure).
    # Three responsibilities:
    #
    #  0. (default on, TAM_ACTION_LOG) log the call in agent_actions
    #     (tam_hook.py record-action, src/ambient/actions.py): tool, short
    #     redacted summary, exit status. Synchronous so the Stop hook sees it.
    #  1. (always-on) bump per-session counters (writes / edits / saves)
    #     and, when the writes-without-saves ratio crosses a threshold,
    #     send a nudge to the model as hookSpecificOutput.additionalContext.
    #     This addresses the "model never calls memory_save on its own"
    #     pattern (reported 2026-05-14 by client running Sonnet).
    #  2. (opt-in) when MEMORY_POST_TOOL_CAPTURE=1, enqueue a deferred
    #     observation for the extractor (detached worker).
    #
    # Env:
    #   MEMORY_POST_TOOL_CAPTURE  — "1" to enable observation capture
    #   MEMORY_NUDGE_DISABLE      — "1" to disable nudges entirely
    #   MEMORY_NUDGE_SOFT/HARD/STEP — tune thresholds (see lib/tam_hook.py)
    #   TAM_MEMORY_DIR / CLAUDE_MEMORY_INSTALL_DIR — resolved by lib/common.sh
    #
    # Hook: PostToolUse (matcher: "*"), PostToolUseFailure (matcher: "*")
    # ===========================================
    
    HOOK_PARSE_FIELDS=0
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    
    printf '%s' "$HOOK_INPUT" | hook_helper record-action --src "$HOOK_SRC_DIR" --memory-dir "$CLAUDE_MEMORY_DIR"
    
    # Nudge counter + emission (synchronous; stdlib only, no DB or network I/O).
    if [ "${MEMORY_NUDGE_DISABLE:-0}" != "1" ]; then
        printf '%s' "$HOOK_INPUT" | hook_helper nudge --memory-dir "$CLAUDE_MEMORY_DIR" --emit PostToolUse
    fi
    
    # Opt-in guard — observation capture only runs when explicitly enabled.
    if [ "${MEMORY_POST_TOOL_CAPTURE:-0}" != "1" ]; then
        exit 0
    fi
    
    # Cache stdin so the detached worker can read it after this shell exits.
    TMP_INPUT="$(mktemp -t cmm-pthook.XXXXXX)" || { hook_log "POST_TOOL_CAPTURE: mktemp failed"; exit 0; }
    printf '%s' "$HOOK_INPUT" > "$TMP_INPUT"
    
    hook_helper capture-observation --src "$HOOK_SRC_DIR" --memory-dir "$CLAUDE_MEMORY_DIR" \
        --input "$TMP_INPUT" </dev/null >>"$HOOK_LOG_FILE" 2>&1 &
    disown 2>/dev/null || true
    exit 0
    
  • hooks/pre-edit.ps1GitHub
  • hooks/pre-edit.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # PreToolUse hook for Write|Edit — v7.0 file_context guard
    #
    # Asks the model to call file_context(path) BEFORE editing a file.
    # Delivered as hookSpecificOutput.additionalContext (plain stdout of
    # PreToolUse never reaches the model). No permission decision is made.
    #
    # Hook: PreToolUse (matcher: "Write|Edit")
    # ===========================================
    
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    hook_set_event "PreToolUse"
    
    TOOL=$(hook_get '.tool_name')
    FILE_PATH=$(hook_get '.tool_input.file_path')
    
    # Only guard Write/Edit, not NotebookEdit
    case "$TOOL" in
        Write|Edit) ;;
        *) exit 0 ;;
    esac
    
    [ -z "$FILE_PATH" ] && exit 0
    
    # Skip vendored / temp paths
    case "$FILE_PATH" in
        */.git/*|*/node_modules/*|*/.venv/*|/tmp/*) exit 0 ;;
    esac
    
    hook_add_context "v7.0 pre-edit guard: before editing \`$FILE_PATH\`, call
      file_context(path=\"$FILE_PATH\")
    If risk_score > 0.3, read the returned warnings (past errors / hot spots) and
    incorporate them into the edit. Skip if file_context was already called for this
    path in the current turn."
    
    exit 0
    
  • hooks/rule-reminder.ps1GitHub
  • hooks/rule-reminder.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # PreToolUse Hook — rule reminder before a Bash call or a file edit
    #
    # Matches the command line (or the edited path plus the new text) against the
    # active SOUL rules (tam_hook.py rule-reminder, src/ambient/reminders.py).
    # A matching rule — above all a prohibition the action seems to break — is
    # sent to the model as hookSpecificOutput.additionalContext and recorded as a
    # rule surface, so the Stop hook can check whether it was followed. The same
    # rule is reminded at most once per 15 minutes per session. No permission
    # decision is ever made.
    #
    # Env: TAM_RULE_REMINDERS=false disables it (also settable in the dashboard)
    #
    # Hook: PreToolUse (matcher: "Bash|Edit|Write|MultiEdit|NotebookEdit")
    # ===========================================
    
    HOOK_PARSE_FIELDS=0
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    
    printf '%s' "$HOOK_INPUT" | hook_helper rule-reminder --src "$HOOK_SRC_DIR" \
        --memory-dir "$CLAUDE_MEMORY_DIR" --emit PreToolUse
    exit 0
    
  • hooks/session-end.ps1GitHub
  • hooks/session-end.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # ===========================================
    # Session End Hook — Portable version
    #
    # 1. Parses the transcript (nested Claude Code JSONL and the legacy flat
    #    format, via src/auto_episode_capture.extract_messages_from_transcript)
    # 2. Writes this session's recovery file (one per session)
    # 3. Starts auto_session_save.py and auto_episode_capture.py in background
    # 4. Cleans error-fix state
    #
    # SessionEnd output is not shown to the model; progress goes to hooks.log.
    #
    # Hook: SessionEnd (matcher: "")
    # ===========================================
    
    # shellcheck source=lib/common.sh
    source "$(dirname "$0")/lib/common.sh"
    
    REASON=$(hook_get 'reason')
    CTX=$(hook_context)
    
    # Human-readable reason
    case "$REASON" in
        clear)                       REASON_TEXT="User ran /clear" ;;
        compact)                     REASON_TEXT="User ran /compact" ;;
        logout)                      REASON_TEXT="User logged out" ;;
        prompt_input_exit)           REASON_TEXT="User exited" ;;
        bypass_permissions_disabled) REASON_TEXT="Permissions changed" ;;
        *)                           REASON_TEXT="Session ended (${REASON:-unknown})" ;;
    esac
    
    printf '%s' "$HOOK_INPUT" | hook_helper session-end \
        --src "$HOOK_SRC_DIR" \
        --recovery-dir "$HOOK_RECOVERY_DIR" \
        --reason-text "$REASON_TEXT"
    
    hook_notify "$CTX | $REASON_TEXT" "Claude Memory | Done" "Submarine"
    hook_log "SESSION_END: $CTX | $REASON_TEXT"
    
    # ======= CLEAN UP ERROR-FIX STATE =======
    if [ -d "$HOOK_STATE_DIR" ]; then
        rm -f "$HOOK_STATE_DIR"/last-error-* 2>/dev/null
    fi
    exit 0
    
  • hooks/session-start.ps1GitHub
  • hooks/session-start.shRunsGitHub
  • hooks/user-prompt-submit.ps1GitHub
  • hooks/user-prompt-submit.shRunsGitHub

All 24 scripts are listed above. The source is inlined for 6 of them, starting with whatever hooks.json actually runs. See all of them in the repo.

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.

@vbcherepanov
Ships withtotal-agent-memory

Persistent local memory for AI coding agents — Claude Code, Codex CLI, Cursor, any MCP client. Temporal knowledge graph, procedural memory, AST codebase ingest, cross-project analogy. LongMemEval R@5 95.1%, LoCoMo 0.607, BEAM 1M 0.448. 74 MCP tools, 9 IDEs, 100% local.

754 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
75
Star history of vbcherepanov/total-agent-memory 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 total-agent-memory.

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