Skip to content
Development
Hook

Hooks

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

From plugin
claude-token-reducer
521 skill4 agents1 command1 hook
+1

Claude Code

> /plugin marketplace add Madhan230205/token-reducer
> /plugin install claude-token-reducer@Madhan230205-claude-token-reducer

What fires, and when

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.

  • python ${CLAUDE_PLUGIN_ROOT}/hooks/userprompt_guard.py
Read hooks/hooks.json

In the plugin's words

How claude-token-reducer describes its own hook set.

token-reducer prompt guardrails for bypass prevention and session hygiene reminders

Where it lives

  • hooks/userprompt_guard.pyRunsGitHub
    Read the script
    #!/usr/bin/env python3
    """User prompt guardrails for token-reducer.
    
    Goals:
    1) Warn when user appears to paste very large raw content (pipeline bypass risk).
    2) Periodically remind to compact/start fresh sessions to avoid context drift.
    """
    
    from __future__ import annotations
    
    import json
    import os
    import subprocess
    import sys
    from pathlib import Path
    from typing import Any
    
    
    MAX_PROMPT_WORDS = 900
    MAX_PROMPT_LINES = 120
    HARD_TRUNCATE_WORDS = 800
    HARD_BLOCK_WORDS = 3000
    REMINDER_TURNS_DEFAULT = {5, 8, 10, 12, 15, 20, 28, 36}
    AUTO_COMPACT_TURN_DEFAULT = 10
    AUTO_RESET_TURN_DEFAULT = 40
    CRITICAL_RESET_TURN_DEFAULT = 50
    
    
    def _load_guard_settings(plugin_root: str) -> dict[str, Any]:
        path = Path(plugin_root) / "settings.json"
        if not path.is_file():
            return {}
        try:
            data = json.loads(path.read_text(encoding="utf-8"))
        except (OSError, json.JSONDecodeError):
            return {}
        if not isinstance(data, dict):
            return {}
        pg = data.get("promptGuard")
        tr = data.get("tokenReducer")
        out: dict[str, Any] = {}
        if isinstance(pg, dict):
            out["promptGuard"] = pg
        if isinstance(tr, dict):
            out["tokenReducer"] = tr
        return out
    
    
    def _guard_params(plugin_root: str) -> tuple[int, int, int, set[int]]:
        blob = _load_guard_settings(plugin_root)
        pg = blob.get("promptGuard") or {}
        tr = blob.get("tokenReducer") or {}
    
        auto_compact = int(pg.get("autoCompactTurn", AUTO_COMPACT_TURN_DEFAULT))
        auto_reset = int(pg.get("autoResetTurn", AUTO_RESET_TURN_DEFAULT))
        critical = int(pg.get("criticalResetTurn", CRITICAL_RESET_TURN_DEFAULT))
    
        reminder: set[int] = set()
        if isinstance(pg.get("reminderTurns"), list):
            for x in pg["reminderTurns"]:
                try:
                    reminder.add(int(x))
                except (TypeError, ValueError):
                    pass
        hist = tr.get("historyCompactReminderTurns")
        if isinstance(hist, list):
            for x in hist:
                try:
                    reminder.add(int(x))
                except (TypeError, ValueError):
                    pass
        if not reminder:
            reminder = set(REMINDER_TURNS_DEFAULT)
    
        return auto_compact, auto_reset, critical, reminder
    
    
    def estimate_tokens(text: str) -> int:
        return max(1, int(len(text.split()) * 1.3))
    
    
    def extract_prompt(payload: Any) -> str:
        if isinstance(payload, dict):
            for key in ("user_prompt", "prompt", "message", "input"):
                value = payload.get(key)
                if isinstance(value, str):
                    return value
            for value in payload.values():
                extracted = extract_prompt(value)
                if extracted:
                    return extracted
            return ""
    
        if isinstance(payload, list):
            for item in payload:
                extracted = extract_prompt(item)
                if extracted:
                    return extracted
            return ""
    
        return payload if isinstance(payload, str) else ""
    
    
    def extract_session_id(payload: Any) -> str:
        if isinstance(payload, dict):
            for key in ("session_id", "sessionId", "conversation_id", "conversationId"):
                value = payload.get(key)
                if isinstance(value, str) and value.strip():
                    return value.strip()
            for value in payload.values():
                sid = extract_session_id(value)
                if sid:
                    return sid
        elif isinstance(payload, list):
            for item in payload:
                sid = extract_session_id(item)
                if sid:
                    return sid
        return "default"
    
    
    def load_state(path: Path) -> dict[str, Any]:
        if not path.exists():
            return {"sessions": {}}
        try:
            return json.loads(path.read_text(encoding="utf-8"))
        except Exception:
            return {"sessions": {}}
    
    
    def save_state(path: Path, state: dict[str, Any]) -> None:
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
    
    
    def main() -> int:
        try:
            payload = json.load(sys.stdin)
        except Exception:
            print(json.dumps({}), file=sys.stdout)
            return 0
    
        prompt = extract_prompt(payload)
        session_id = extract_session_id(payload)
    
        plugin_root = os.environ.get("CLAUDE_PLUGIN_ROOT", "")
        state_path = Path(plugin_root) / ".cache" / "token-reducer" / "prompt_guard_state.json"
        state = load_state(state_path)
        sessions = state.setdefault("sessions", {})
        turns = int(sessions.get(session_id, 0)) + 1
        sessions[session_id] = turns
        state["sessions"] = sessions
        try:
            save_state(state_path, state)
        except Exception:
            pass
    
        auto_compact_turn, auto_reset_turn, critical_reset_turn, reminder_turns = _guard_params(
            plugin_root
        )
    
        messages: list[str] = []
        result: dict[str, Any] = {}
    
        if prompt:
            word_count = len(prompt.split())
            line_count = prompt.count("\n") + 1
            has_compact_packet = "CONTEXT_PACKET_START" in prompt
            has_token_reducer_intent = "/token-reducer" in prompt or "token-reducer" in prompt.lower()
            bypass = has_compact_packet or has_token_reducer_intent
    
            if not bypass and word_count > HARD_BLOCK_WORDS:
                # Hard block: reject the prompt entirely
                messages.append(
                    f"🚫 Prompt BLOCKED: {word_count} words (~{estimate_tokens(prompt)} tokens) exceeds the "
                    f"{HARD_BLOCK_WORDS}-word hard limit. Reduce your prompt size or pass large content "
                    "via --inputs and run /token-reducer instead. Prompt was not submitted."
                )
                result["rejectInput"] = True
                result["systemMessage"] = "\n\n".join(messages)
                print(json.dumps(result), file=sys.stdout)
                return 0
    
            if not bypass and word_count > HARD_TRUNCATE_WORDS:
                # TPCH: Zero-Turn Auto-Compression — intercept before LLM sees the prompt
                messages.append(
                    f"⚡ Auto-Compression Engaged: Int

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.

@madhan230205
Ships withclaude-token-reducer

⚡ Cut Claude token usage by 90%+ — free, open-source, local-first context compression for Claude Code. Hybrid RAG (BM25 + ONNX vectors), AST chunking, reranking. No API needed.

521 skill4 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
52
Star history of Madhan230205/token-reducer 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 claude-token-reducer.

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