Skip to content
Development
Hook

Hooks

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

From plugin
dotnet-episteme-skills
1314 skills13 agents3 commands3 hooks
+1

Claude Code

> /plugin marketplace add Metalnib/dotnet-episteme-skills
> /plugin install dotnet-episteme-skills@dotnet-episteme-marketplace

What fires, and when

PreToolUse

  • MatchesBash"${CLAUDE_PLUGIN_ROOT}"/hooks/git-readonly-guard.sh

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"${CLAUDE_PLUGIN_ROOT}"/hooks/design-state-reload.sh
  • Matchescompact"${CLAUDE_PLUGIN_ROOT}"/hooks/design-state-reload.sh
  • Matchesclear"${CLAUDE_PLUGIN_ROOT}"/hooks/design-state-reload.sh
  • Matchesresume"${CLAUDE_PLUGIN_ROOT}"/hooks/design-state-reload.sh

PreCompact

  • Matchesauto
  • Matchesmanual
Read hooks/hooks.json

Where it lives

  • hooks/design-state-reload.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # SessionStart hook (matchers: startup, compact, clear, resume) for the dotnet-refactor pipeline.
    # On a fresh session, and after compaction/clear/resume, re-inject the active DESIGN state file
    # so the loop resumes from the artifact instead of from lost conversation history.
    set -euo pipefail
    
    dir="${CLAUDE_PROJECT_DIR:-$PWD}/.episteme"
    
    # Newest DESIGN-*.md whose frontmatter is not `status: done`. The status line
    # must be read from the YAML frontmatter only - the Decision log body carries
    # status/phase entries too, so a whole-file grep would skip an active loop.
    # The .episteme/ convention is shared with commands/dotnet-refactor.md.
    state_file=""
    for f in "$dir"/DESIGN-*.md; do
      [ -e "$f" ] || continue
      # Frontmatter = lines between the line-1 `---` fence and the next `---`.
      frontmatter="$(awk 'NR==1 && $0!="---"{exit} $0=="---"{n++; next} n==1{print} n>=2{exit}' "$f" 2>/dev/null)"
      printf '%s\n' "$frontmatter" | grep -Eq '^status:[[:space:]]*done[[:space:]]*$' && continue
      if [ -z "$state_file" ] || [ "$f" -nt "$state_file" ]; then
        state_file="$f"
      fi
    done
    
    [ -z "$state_file" ] && exit 0
    
    # Cap what we inject; the file itself stays the source of truth.
    content=$(head -c 16384 "$state_file")
    
    python3 - "$state_file" <<'PY' "$content"
    import json, sys
    path, content = sys.argv[1], sys.argv[2]
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "SessionStart",
            "additionalContext": (
                f"An active /dotnet-refactor design loop was found at {path}. "
                f"Resume from the phase and status its frontmatter records; the file is the source of truth, not prior conversation.\n\n"
                f"{content}"
            ),
        }
    }))
    PY
    
  • hooks/git-readonly-guard.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    # PreToolUse guard: this plugin's worker agents (review, refactor and qa lanes)
    # may only run read-only git and synopsis commands. Other agents and the main
    # session pass through untouched.
    set -euo pipefail
    
    INPUT="$(cat)"
    
    # Subagent calls carry agent_type on both Claude Code and Codex (>=0.145);
    # main-session calls don't - skip those without spawning python3.
    case "$INPUT" in
      *'"agent_type"'*) ;;
      *) exit 0 ;;
    esac
    
    PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
    python3 - "$INPUT" "$PLUGIN_ROOT" <<'PY'
    import json, os, re, shlex, sys
    
    data = json.loads(sys.argv[1])
    agent = data.get("agent_type") or ""
    # Exact lane sets (Claude namespaced, Codex flat) - a user's unrelated
    # "review-*"/"qa-*" role must not be restricted.
    GROUPS = {
        "review": {"correctness", "performance", "security-observability",
                   "data-messaging", "generalist", "maintainer"},
        "refactor": {"cartographer", "tracer", "conformance-auditor", "surveyor"},
        "qa": {"acceptance", "reuse-design", "dead-code"},
    }
    matched_group = next((group for group, lanes in GROUPS.items()
                          for p in (f"dotnet-episteme-skills:{group}:", f"{group}-")
                          if agent.startswith(p) and agent[len(p):] in lanes), None)
    if matched_group is None:
        # Not one of this plugin's worker agents (main session, other agents,
        # skills): exit 0 = no opinion, the normal permission flow applies.
        sys.exit(0)
    
    cmd = (data.get("tool_input") or {}).get("command") or ""
    # No chaining/redirection/substitution - read-only commands need none of it.
    if re.search(r"[;&|<>`$]", cmd) or "\n" in cmd:
        print("Blocked for plugin worker agents: shell operators are not allowed.", file=sys.stderr)
        sys.exit(2)
    # Every worker group gets the read-only search/list tools on top of git and
    # synopsis - fast tools (rg, fd) plus the GNU fallbacks, and `command -v`/`which`
    # to probe which is installed. Review and qa lanes used to be denied these, which
    # only cost them a run of failed calls before they fell back to the Grep tool:
    # the native Read/Grep/Glob tools are not seen by this hook at all, so denying the
    # shell equivalents bought no confinement it did not already lack.
    #
    # The shell-operator block above stops chaining/redirection; but several of
    # these tools can execute or write through their OWN flags with no operator at
    # all (rg --pre runs a command per file, tree -o writes a file, file -C
    # compiles one). Each tool's write/exec flags are denied explicitly. Short
    # flags cluster (-Cm is -C -m, -zi is -z -i), so the short-flag half of each
    # pattern matches the letter anywhere in a leading cluster; the letter sets are
    # per-tool so a safe flag on one tool (rg -x = --line-regexp) is not blocked
    # because it is dangerous on another (fd -x = --exec). `-o` is per-tool too:
    # rg/grep use it for --only-matching (safe), tree uses it to write (denied).
    DANGER_FLAGS = {
        "rg":   r"(^|\s)(--pre|--pre-glob|--hostname-bin|--search-zip)(\s|=|$)|(^|\s)-[A-Za-z]*z",
        "tree": r"(^|\s)(-o|--output)",
        "file": r"(^|\s)(--compile)(\s|=|$)|(^|\s)-[A-Za-z]*C",
        "find": r"(^|\s)(-delete|-exec\w*|-ok\w*|-fprint\w*|-fls)(\s|$)",
        "fd":   r"(^|\s)(--exec|--exec-batch)(\s|=|$)|(^|\s)-[A-Za-z]*[xX]",
    }
    if matched_group in ("refactor", "review", "qa"):
        if re.match(r"^\s*(command\s+-v|which)\s", cmd):
            sys.exit(0)
        tm = re.match(r"^\s*(rg|fd|grep|ls|eza|cat|head|tail|wc|tree|file|stat|find)\b", cmd)
        if tm:
            danger = DANGER_FLAGS.get(tm.group(1))
            if danger and re.search(danger, cmd):
                print(f"Blocked for plugin worker agents: {tm.group(1)} write/exec flags are not allowed.", file=sys.stderr)
                sys.exit(2)
            # Confine reads to the project. An absolute path is allowed when it
            # resolves inside the project directory - the same rule `git -C` already
            # uses below, and what a worker naturally writes when it knows the repo
            # root. Parent-directory escapes stay banned outright. NOTE this governs
            # the SHELL only - the native Read/Grep/Glob tools are not seen by this
            # hook (see docs/reviewer-restrictions.md), so on Claude Code this is
            # defence in depth, not an absolute read sandbox.
            try:
                tokens = shlex.split(cmd)
            except ValueError:
                print("Blocked for plugin worker agents: could not parse the command safely.", file=sys.stderr)
                sys.exit(2)
            # Codex has no CLAUDE_PROJECT_DIR; its payload carries the turn cwd instead.
            proj = os.environ.get("CLAUDE_PROJECT_DIR") or data.get("cwd")
    
            def inside_project(path):
                if not proj:
                    return False
                real = os.path.realpath(os.path.expanduser(path))
                proj_real = os.path.realpath(proj)
                return real == proj_real or real.startswith(proj_real + os.sep)
    
            for tok in tokens[1:]:
                if tok.startswith("-"):
                    fv = re.search(r"=([~/].*)$", tok)
                    if fv and not inside_project(fv.group(1)):
                        print("Blocked for plugin worker agents: that flag value points outside the project directory.", file=sys.stderr)
                        sys.exit(2)
                    continue
                if tok.startswith("/") or tok.startswith("~"):
                    if not inside_project(tok):
                        print("Blocked for plugin worker agents: that path is outside the project directory; search within the project or use the Read/Grep tools.", file=sys.stderr)
                        sys.exit(2)
                    continue
                if tok == ".." or tok.startswith("../") or "/../" in tok or tok.endswith("/.."):
                    print("Blocked for plugin worker agents: parent-directory escapes are not allowed.", file=sys.stderr)
                    sys.exit(2)
            sys.exit(0)
    # --output as a whole flag only: git's read-only --output-indicator-{new,old,context}
    # write nothing and must not be bl

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.

@metalnib
Ships withdotnet-episteme-skills

DotNet Episteme Skills - a curated, manual-first .NET AI skills library rooted in systematic knowledge (episteme) and shaped by disciplined craft (techne), designed for engineers who prioritise precision over hype.

1314 skills13 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
13
Star history of Metalnib/dotnet-episteme-skills 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 hook318k
@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 hooks191k
@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+312k
@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 dotnet-episteme-skills.

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