Skip to content
Development
Hook

Hooks

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

From plugin
token-optimizer
1.8k9 skills2 commands10 hooks
Install
> /plugin marketplace add alexgreensh/token-optimizer
> /plugin install token-optimizer@alexgreensh-token-optimizer

Ships with token-optimizer. Installing the plugin gets these hooks.

What fires, and when

PreToolUse

  • MatchesReadfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/read_cache.py --quiet; done; exit 0
  • MatchesBashfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/bash_hook.py --quiet; done; exit 0
  • MatchesAgent|Taskfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py checkpoint-trigger --milestone pre-fanout
  • Matchesmcp__.*for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/refetch_guard.py --quiet; done; exit 0

PreCompact

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py dynamic-compact-instructions --quiet; done
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py compact-capture --trigger auto --quiet; do
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/read_cache.py --clear --quiet; done; exit 0

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.

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py ensure-health; done; exit 0
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py quality-cache --force --quiet; done; exit
  • Matchescompactfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py compact-restore --compact; done; exit 0for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/read_cache.py --clear-compacted --quiet; done; exit 0
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py compact-restore --new-session-only; done;

Stop

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py compact-capture --trigger stop --quiet; dofor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py session-end-flush --trigger stop --quiet -for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py keepwarm-arm --quiet; done; exit 0

SessionEnd

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py session-end-flush --trigger end --defer; d

StopFailure

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py compact-capture --trigger stop-failure --q

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.

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py quality-cache --warn --quiet; done; exit 0
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py prompt-continuity --quiet; done; exit 0
  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py verbosity-steer --quiet; done; exit 0

PostToolUse

  • Matchesmcp__.*for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/archive_result.py --quiet; done; exit 0
  • MatchesBash|Read|Glob|Grep|Agentfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/archive_result.py --quiet; done; exit 0
  • MatchesBash|Read|Grep|Glob|mcp__.*for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/context_intel.py --quiet; done; exit 0
  • MatchesEdit|Write|MultiEdit|NotebookEditfor b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/read_cache.py --invalidate --quiet; done; exit 0
  • MatchesBash|Read|Glob|Grep|Agent|Edit|Write|MultiEdit|NotebookEdit|mcp_for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py quality-cache --quiet --throttle-only; don

PostCompact

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/measure.py quality-cache --force --quiet; done; exit

CwdChanged

  • for b in bash /bin/bash /usr/bin/bash /usr/local/bin/bash /opt/homebrew/bin/bash; do command -v "$b" >/dev/null 2>&1 && exec "$b" "${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh" "${CLAUDE_PLUGIN_ROOT}/hooks/run.py" skills/token-optimizer/scripts/read_cache.py --clear --quiet; done; exit 0
Read hooks/hooks.json

Where it lives

  • hooks/module_runner.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """Runs a hook script as a module instead of as `__main__`, so CPython reuses
    its __pycache__ bytecode across invocations.
    
    Script-mode execution (`python foo.py`) never checks or writes __pycache__ for
    the file being run as __main__ -- only for things it imports. Since every hook
    call is a fresh process, running measure.py (35k+ lines) as a script recompiles
    it from source every single time: ~0.3s of pure CPython parse/compile on top of
    whatever the hook actually does. Module-mode goes through the import system,
    which does check/write __pycache__, cutting that to ~0.1s after the first call.
    
    sys.path is stripped of '' and '.' (the cwd-equivalent entries the interpreter
    would otherwise add) before inserting scripts_dir explicitly, so a same-named
    file in the invoking project's own working directory (e.g. a project that
    happens to have its own measure.py at its root) can never shadow the plugin's
    module.
    """
    from __future__ import annotations
    
    import os
    import runpy
    import sys
    
    
    def _warn_readonly_scripts_dir_once(scripts_dir: str) -> None:
        """If scripts_dir isn't writable, __pycache__ can't be written and CPython
        recompiles the target from source on every call — the perf win silently
        evaporates. Surface it, but at most once per day per user so we don't spam
        stderr on the per-tool-call hot path. The marker lives in the OS temp dir
        (writable even when the plugin install dir is read-only, which is the whole
        failure mode). Best-effort: any error here must never break the hook."""
        try:
            if os.access(scripts_dir, os.W_OK):
                return  # normal case — bytecode cache works, stay silent
            import hashlib
            import tempfile
            import time
    
            tag = hashlib.sha1(scripts_dir.encode("utf-8", "replace")).hexdigest()[:12]
            marker = os.path.join(tempfile.gettempdir(), f".token-optimizer-ro-pyc-{tag}")
            try:
                fresh = (time.time() - os.path.getmtime(marker)) < 86400
            except OSError:
                fresh = False
            if fresh:
                return
            with open(marker, "w", encoding="utf-8") as fh:
                fh.write(str(time.time()))
            sys.stderr.write(
                f"[Token Optimizer] note: {scripts_dir} is not writable, so Python "
                "bytecode (__pycache__) can't be cached — hooks will be slower than "
                "expected. Make the plugin scripts dir writable to restore the speedup.\n"
            )
        except Exception:
            pass
    
    
    def main() -> int:
        if len(sys.argv) < 3:
            return 0
    
        scripts_dir = sys.argv[1]
        module_name = sys.argv[2]
        script_args = sys.argv[3:]
    
        # Defense-in-depth: don't rely solely on run.py sanitizing these. Refuse a
        # module_name that isn't a bare Python identifier (blocks path separators,
        # dots, and traversal), and require the resolved target to actually exist in
        # scripts_dir. Fail OPEN (return 0) rather than raise — a hook must never
        # crash the tool call. Without this, any future caller that skipped run.py's
        # own validation would hand runpy an arbitrary-module-execution primitive.
        if not module_name.isidentifier():
            return 0
        if not os.path.isfile(os.path.join(scripts_dir, module_name + ".py")):
            return 0
    
        _warn_readonly_scripts_dir_once(scripts_dir)
    
        sys.path = [p for p in sys.path if p not in ("", ".")]
        sys.path.insert(0, scripts_dir)
        sys.argv = [module_name, *script_args]
    
        runpy.run_module(module_name, run_name="__main__", alter_sys=True)
        return 0
    
    
    if __name__ == "__main__":
        sys.exit(main())
    
  • hooks/python-launcher.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # Locate a usable Python 3 interpreter and exec it with the given arguments.
    # Survives:
    #   - macOS / Linux (python3 on PATH)
    #   - Windows python.org installs at spaced paths like "C:\Program Files\Python313\"
    #   - Windows py-launcher-only installs (py -3)
    #   - Windows Store Python (real installs proven alive via a flash-free
    #     GUI-twin proof-of-life probe, console --version probe as the fallback
    #     authority; non-functional AppExecutionAlias stubs skipped automatically)
    # On Windows (Git Bash/MSYS), exec prefers the GUI-subsystem twin over the
    # console binary to avoid the per-hook console-window flash and orphaned
    # conhost.exe: python.exe/python3.exe swap to pythonw.exe, and py.exe (the
    # py-launcher) swaps to pyw.exe beside it, so py-launcher-only installs no
    # longer flash (#107). See _maybe_swap_to_pythonw for the constraints.
    # Exits 127 with a diagnostic message if none found.
    
    set -eu
    # C7: extglob enables +([0-9]) in the version-number case patterns below so
    # the glob is anchored to the path-component boundary. Without it, * in a
    # case pattern crosses / and Python[23]* matches Python3-evil/python.exe.
    shopt -s extglob
    
    # Known-safe prefixes for Python interpreter binaries.
    # Binaries outside these directories are rejected even if on PATH.
    # This prevents a compromised PATH entry from hijacking the interpreter.
    # All prefixes are hardcoded (not derived from PATH-controlled binaries
    # like `brew --prefix`, which would be circular trust).
    _SAFE_PREFIXES="/usr/bin /usr/local/bin /opt/homebrew/bin /opt/homebrew/opt /home/linuxbrew/.linuxbrew/bin"
    
    _is_safe_prefix() {
        local IFS=$' \t\n'
        local binpath="$1" prefix
        # Reject path traversal FIRST: a '..' component lets a textual prefix match
        # (e.g. /usr/bin/../../tmp/evil/python3) pass the allow-list globs below yet
        # resolve OUTSIDE a trusted dir at exec time. Interpreter paths are absolute
        # and never legitimately contain a '..' path component.
        case "$binpath" in
            *"/../"*|*"/..") return 1 ;;
        esac
        for prefix in $_SAFE_PREFIXES; do
            case "$binpath" in
                "$prefix"/*) return 0 ;;
            esac
        done
        # Windows install locations (git-bash/MSYS path form, e.g. /c/...).
        # Drive-letter-anchored to preserve the anti-PATH-hijack intent.
        # Version-number suffixes block directory-name spoofing (e.g. Python3-evil).
        case "$binpath" in
            # C7: +([0-9]) anchors the version suffix to digits-only so a spoofed
            # dir name like Python3-evil cannot pass (previously * crossed / and
            # matched Python3-evil/python.exe). The trailing /* requires a path
            # separator after the version component.
            /[a-zA-Z]/Program\ Files/Python[23]+([0-9])/*)                 return 0 ;;
            /[a-zA-Z]/Program\ Files\ \(x86\)/Python[23]+([0-9])/*)        return 0 ;;
            /[a-zA-Z]/Python3+([0-9])/*)                                   return 0 ;;
            /[a-zA-Z]/Users/*/AppData/Local/Programs/Python/*)              return 0 ;;
            /[a-zA-Z]/Users/*/AppData/Local/Microsoft/WindowsApps/*)        return 0 ;;
            # All-users `py` launcher lives in the (admin-only-writable) Windows dir.
            # Exact filename keeps the anti-hijack intent (no wildcard in that dir).
            /[a-zA-Z]/Windows/py.exe)                                      return 0 ;;
            /[a-zA-Z]/Windows/pyw.exe)                                     return 0 ;;
        esac
        # C8: case-insensitive WindowsApps allow for Windows-style drive-letter
        # paths. On a case-insensitive FS the dir can be any casing; the drive-
        # letter anchor ([a-zA-Z]/) constrains this to Windows-style paths so
        # Linux is unaffected.
        case "$binpath" in
            /[a-zA-Z]/*)
                _path_contains_windowsapps "$binpath" && return 0
                ;;
        esac
        return 1
    }
    
    # The cache is an optional optimization only. Any setup, read, or write failure
    # leaves _PY_CACHE_FILE empty (or is ignored) so interpreter discovery proceeds
    # exactly as it did before caching was added.
    _PY_CACHE_FILE=""
    
    _is_msys_platform() {
        local platform
        platform=$(uname -s 2>/dev/null) || return 1
        case "$platform" in
            MINGW*|MSYS*|CYGWIN*) return 0 ;;
        esac
        return 1
    }
    
    # C8: Windows is case-insensitive, so the WindowsApps directory can appear in
    # any casing (WindowsApps, windowsapps, WINDOWSAPPS, Windowsapps). The old
    # explicit-variant patterns (*/WindowsApps/*|*/windowsapps/*) only covered
    # two casings and would miss others, letting a Store AppExecutionAlias stub
    # through unprobed or skipping a legit Store install in the safe-prefix list.
    # tr is POSIX and present in every supported hook env including Git Bash.
    _path_contains_windowsapps() {
        local lower
        lower=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
        case "$lower" in
            */windowsapps/*) return 0 ;;
        esac
        return 1
    }
    
    _cache_dir_is_per_user() {
        local cache_dir="$1" cache_real root root_real
        cache_real=$(CDPATH='' cd -- "$cache_dir" 2>/dev/null && pwd -P) || return 1
        for root in "${XDG_CACHE_HOME:-}" "${HOME:-}"; do
            [ -n "$root" ] && [ -d "$root" ] || continue
            root_real=$(CDPATH='' cd -- "$root" 2>/dev/null && pwd -P) || continue
            case "$cache_real" in
                "$root_real"/*) return 0 ;;
            esac
        done
        return 1
    }
    
    _cache_dir_ready() {
        local cache_dir="$1"
        if [ ! -d "$cache_dir" ]; then
            (umask 077; mkdir -p "$cache_dir") >/dev/null 2>&1 || return 1
        fi
        [ -d "$cache_dir" ] && [ -w "$cache_dir" ] || return 1
        if _is_msys_platform; then
            # Git Bash/MSYS2/Cygwin emulate POSIX ownership, so `test -O` can reject
            # the current user's own directory. Canonical confinement under HOME or
            # XDG_CACHE_HOME supplies the equivalent Windows ACL trust boundary.
            _cache_dir_is_per_user "$cache_dir"
        else
            # On native POSIX, require ownership rather than mere writability so a
            # planted 
  • hooks/run.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """Cross-platform hook dispatcher.
    
    Invoked from hooks.json via a small bash launcher that locates a usable
    Python 3 interpreter on macOS, Linux, and Windows:
    
      "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/python-launcher.sh\" \"${CLAUDE_PLUGIN_ROOT}/hooks/run.py\" <script-relative-path> [args...]"
    
    The launcher handles Windows-specific gotchas (Program Files spaced paths,
    Microsoft Store zero-byte stubs in WindowsApps, py launcher fallback) so
    this file can assume it's running under a real Python 3.9+.
    
    This dispatcher resolves the target script under CLAUDE_PLUGIN_ROOT,
    checks it exists, and runs it with the same interpreter (sys.executable).
    On timeout we kill the child (Popen.kill) to avoid leaking a process
    holding the trends.db SQLite lock. Always exits 0 so hook failures never
    block the user's tool call.
    
    Windows reap note: module_runner.py runs measure.py IN-PROCESS via
    runpy.run_module, so the child proc IS measure.py (the trends.db lock
    holder), not a grandchild. On Windows we reap with plain proc.kill()
    (TerminateProcess of proc.pid only), NOT taskkill /F /T which would walk
    the PPID tree and wrongly kill the detached session-end-flush worker
    (the one CREATE_BREAKAWAY_FROM_JOB exists to keep alive). The SIGINT/
    SIGTERM handler only fires for console Ctrl+C or in-process os.kill; an
    external TerminateProcess from the host bypasses Python handlers entirely.
    """
    from __future__ import annotations
    
    import json
    import os
    import signal
    import subprocess
    import sys
    from pathlib import Path
    
    # Defense in depth: the launcher script already filters interpreters, but
    # if a user's PATH has a stale Python 3.7 that slipped through, bail early
    # so later imports don't explode with confusing SyntaxError noise.
    if sys.version_info < (3, 9):
        sys.exit(0)
    
    
    # Module-level handle so the signal handler can reach the active child when
    # Claude Code (or any parent) sends SIGTERM/SIGINT to run.py itself. Without
    # this, an external kill reaps run.py but orphans the measure.py grandchild,
    # which keeps the inherited stdout pipe open and makes the parent hang waiting
    # for EOF (the multi-minute stop-hook hang).
    _child_proc: subprocess.Popen | None = None
    
    
    def _reap(proc, posix_sig):
        """Reap the child process. Never raises.
    
        On Windows, the child proc IS measure.py (module_runner.py runs it
        in-process via runpy.run_module), so a plain ``proc.kill()``
        (TerminateProcess of proc.pid only) releases the trends.db lock without
        walking the PPID tree and killing the detached session-end-flush worker
        (the one CREATE_BREAKAWAY_FROM_JOB exists to keep alive).
    
        On POSIX, the child is started with ``start_new_session=True`` so it leads
        its own process group; killing the group reaps any grandchildren (the
        launcher chain uses ``exec``, so run.py's PID is the one the host tracks).
        Falls back to ``proc.kill()`` when the group is already gone.
        """
        if proc.poll() is not None:
            return
        if os.name == "nt":
            try:
                proc.kill()
            except OSError:
                try:
                    sys.stderr.write("run.py: nt reap kill failed\n")
                    sys.stderr.flush()
                except (OSError, ValueError):
                    pass
        elif hasattr(os, "killpg"):
            try:
                os.killpg(os.getpgid(proc.pid), posix_sig)
            except (ProcessLookupError, OSError):
                try:
                    proc.kill()
                except OSError:
                    try:
                        sys.stderr.write("run.py: posix reap kill failed\n")
                        sys.stderr.flush()
                    except (OSError, ValueError):
                        pass
        else:
            try:
                proc.kill()
            except OSError:
                try:
                    sys.stderr.write("run.py: fallback reap kill failed\n")
                    sys.stderr.flush()
                except (OSError, ValueError):
                    pass
    
    
    def _forward_and_exit(signum, frame):
        """Forward SIGTERM/SIGINT to the child, then exit.
    
        On Windows this handler only fires for console Ctrl+C or an in-process
        os.kill; an external TerminateProcess from the host bypasses Python
        handlers entirely.
        """
        global _child_proc
        if _child_proc is not None:
            _reap(_child_proc, signal.SIGTERM)
        os._exit(0)
    
    
    def _check_consent() -> bool:
        """Return True if consent is given or assumed. Fail-open on any error."""
        try:
            home = Path.home()
    
            # Resolve config path from env (set by Claude Code before hook invocation)
            plugin_data = os.environ.get("CLAUDE_PLUGIN_DATA", "")
            if plugin_data:
                pd = Path(plugin_data).resolve()
                if not str(pd).startswith(str(home)):
                    return True  # Path outside home = skip (fail-open)
                config_path = pd / "config" / "config.json"
            else:
                # Legacy / Codex fallback
                codex_home = os.environ.get("CODEX_HOME", "")
                if codex_home:
                    ch = Path(codex_home).resolve()
                    if not str(ch).startswith(str(home)):
                        return True
                    config_path = ch / "token-optimizer" / "config.json"
                else:
                    # Honor CLAUDE_CONFIG_DIR (Claude Code's official config-dir
                    # override) before falling back to ~/.claude. Mirrors
                    # runtime_env.claude_home(): accept any absolute, existing,
                    # non-symlink directory (CLAUDE_CONFIG_DIR may legitimately live
                    # OUTSIDE $HOME — containers, CI), reject relative/symlink, else
                    # fall back. The previous str.startswith($HOME) check both
                    # excluded valid out-of-home dirs and sibling-prefix-matched
                    # (/Users/alex-evil passing for /Users/alex).
                    claude_config = os.environ.get("CLAUDE_CONFIG_DIR", "").strip()
                    cc = None
                    if claude_config:
                        candidate = Path(claude_

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.

Ships withtoken-optimizer

Find the ghost tokens. Fix them. Survive compaction. Avoid context quality decay.

Get the whole plugin
Stats
1,835
Stars
151
Forks
Active
Maintenance
Python
Language
4h ago
Last commit
5mo ago
Created

Repo: alexgreensh/token-optimizer