Skip to content

Hooks

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

From plugin
prd
61010 skills1 agent2 hooks1 MCP

Claude Code

> /plugin marketplace add anombyte93/prd-taskmaster
> /plugin install prd@atlas-prd-taskmaster

What fires, and when

PreToolUse

  • Matchesmcp__plugin_prd_go__advance_phasepython3 ${CLAUDE_PLUGIN_ROOT}/hooks/gate_enforcer.py
  • Matchesmcp__atlas-cdd__.*|mcp__atlas-loop__.*python3 ${CLAUDE_PLUGIN_ROOT}/hooks/mode_d_blocker.py

Stop

  • python3 ${CLAUDE_PLUGIN_ROOT}/hooks/evidence_gate.py
Read hooks/hooks.json

In the plugin's words

How prd describes its own hook set.

prd-taskmaster plugin hooks

Where it lives

  • hooks/evidence_gate.pyRunsGitHub
    Read the script
    #!/usr/bin/env python3
    """
    Evidence gate Stop hook. Blocks session end when the EXECUTE phase has
    in-progress tasks with incomplete CDD evidence (fewer evidence_files than
    subtasks).
    
    Reads Stop JSON from stdin, returns a decision on stdout.
    Never crashes — all parsing wrapped in try/except.
    Short-circuits when stop_hook_active is True to avoid infinite block loops
    when Claude Code re-invokes Stop hooks after a block decision.
    No explicit process termination — main() returns and the process ends naturally.
    """
    import json
    import sys
    from pathlib import Path
    
    
    def main():
        try:
            payload = json.loads(sys.stdin.read())
        except Exception:
            # never crash the hook — bad stdin is a no-op (implicit allow)
            print(json.dumps({}))
            return
    
        try:
            # Short-circuit: if Claude Code is re-invoking Stop hooks after a block,
            # always allow to prevent infinite loops.
            if payload.get("stop_hook_active") is True:
                print(json.dumps({}))
                return
    
            pipeline_path = Path(".atlas-ai/state/pipeline.json")
            if not pipeline_path.is_file():
                print(json.dumps({}))
                return
    
            try:
                pipeline = json.loads(pipeline_path.read_text())
            except Exception:
                print(json.dumps({}))
                return
    
            if pipeline.get("current_phase") != "EXECUTE":
                print(json.dumps({}))
                return
    
            tasks_path = Path(".atlas-ai/taskmaster/tasks/tasks.json")
            if not tasks_path.is_file():
                print(json.dumps({}))
                return
    
            try:
                tasks_data = json.loads(tasks_path.read_text())
            except Exception:
                print(json.dumps({}))
                return
    
            tasks = tasks_data.get("master", {}).get("tasks", []) or []
            offenders = []
            for t in tasks:
                if not isinstance(t, dict):
                    continue
                if t.get("status") != "in-progress":
                    continue
                subtasks = t.get("subtasks") or []
                evidence_files = t.get("evidence_files") or []
                if len(subtasks) > 0 and len(evidence_files) < len(subtasks):
                    offenders.append(
                        f"task {t.get('id')}: {len(evidence_files)}/{len(subtasks)} evidence"
                    )
    
            if offenders:
                reason = (
                    f"Incomplete CDD evidence — {len(offenders)} tasks have subtasks "
                    f"without matching evidence: {'; '.join(offenders)}. "
                    f"Run execute-task loop to completion or mark blocked."
                )
                print(json.dumps({"decision": "block", "reason": reason}))
            else:
                print(json.dumps({}))
        except Exception:
            # any unexpected failure — allow, never crash the hook
            print(json.dumps({}))
            return
    
    
    if __name__ == "__main__":
        main()
    
  • hooks/gate_enforcer.pyRunsGitHub
    Read the script
    #!/usr/bin/env python3
    """
    Gate enforcer hook. Blocks mcp__...__advance_phase calls without gate evidence.
    Reads PreToolUse JSON from stdin, returns permissionDecision on stdout.
    Never crashes — all JSON parsing wrapped in try/except.
    No explicit process termination — main() returns and the process ends naturally.
    """
    import json
    import sys
    
    
    def main():
        try:
            payload = json.loads(sys.stdin.read())
        except Exception:
            # never crash the hook — bad stdin is a no-op (implicit allow)
            print(json.dumps({}))
            return
    
        tool_name = payload.get("tool_name", "")
        if not tool_name.endswith("__advance_phase"):
            print(json.dumps({}))
            return
    
        tool_input = payload.get("tool_input", {})
        target = tool_input.get("target")
        evidence = tool_input.get("evidence", {})
    
        # Mirror the same gate logic as pipeline.check_gate (simplified for hook)
        violations = []
        if target == "SETUP":
            vs = evidence.get("validate_setup", {})
            if not vs.get("ready") or vs.get("critical_failures", 1) > 0:
                violations.append("validate_setup must report ready=true and critical_failures==0; evidence must contain validate_setup block")
        elif target == "DISCOVER":
            if not (
                evidence.get("user_approved")
                or (
                    evidence.get("auto_classification") == "CLEAR"
                    and evidence.get("assumptions_documented")
                )
            ):
                violations.append(
                    "DISCOVER gate requires user_approved=true OR (auto_classification==CLEAR AND assumptions_documented=true); evidence missing"
                )
        elif target == "GENERATE":
            if evidence.get("validation_grade") not in ("EXCELLENT", "GOOD"):
                violations.append("validation_grade must be EXCELLENT or GOOD; evidence insufficient")
            if evidence.get("task_count", 0) == 0:
                violations.append("task_count must be > 0; evidence shows no tasks parsed")
            if evidence.get("subtask_coverage", 0) < 1.0:
                violations.append("subtask_coverage must be >= 1.0; evidence shows subtasks incomplete")
        elif target == "HANDOFF":
            if not evidence.get("user_mode_choice"):
                violations.append("user_mode_choice missing from evidence; user must select execution mode before HANDOFF")
            if not evidence.get("plan_file_exists"):
                violations.append("plan_file_exists missing from evidence; plan file must be written before HANDOFF")
        elif target == "EXECUTE":
            # EXECUTE is terminal — individual tasks track own status; no gate check needed
            pass
    
        if violations:
            reason = f"Gate for {target} not passed: {'; '.join(violations)}"
            print(json.dumps({
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": reason,
                }
            }))
        else:
            print(json.dumps({}))
    
    
    if __name__ == "__main__":
        main()
    
  • hooks/mode_d_blocker.pyRunsGitHub
    Read the script
    #!/usr/bin/env python3
    """
    Mode D blocker hook. Blocks mcp__atlas-cdd and mcp__atlas-loop tool calls.
    Mode D (full CDD + atlas-phoenix) is preview-alpha and requires waitlist opt-in.
    Reads PreToolUse JSON from stdin, returns permissionDecision on stdout.
    Never crashes — all JSON parsing wrapped in try/except.
    No explicit process termination — main() returns and the process ends naturally.
    """
    import json
    import sys
    
    
    def main():
        try:
            payload = json.loads(sys.stdin.read())
        except Exception:
            # never crash the hook — bad stdin is a no-op (implicit allow)
            print(json.dumps({}))
            return
    
        tool_name = payload.get("tool_name", "")
    
        # Check if tool is from blocked Mode D namespaces
        if tool_name.startswith("mcp__atlas-cdd__") or tool_name.startswith("mcp__atlas-loop__"):
            reason = (
                "Mode D (full CDD + atlas-phoenix integration) is preview-alpha and requires waitlist opt-in. "
                "Please join the waitlist at https://atlas-ai.au/waitlist/mode-d to access this feature."
            )
            print(json.dumps({
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": reason,
                }
            }))
        else:
            print(json.dumps({}))
    
    
    if __name__ == "__main__":
        main()
    

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.

@anombyte93
Ships withprd

prd-taskmaster by Atlas AI is an open-source engine for Claude Code that takes a one-line goal, interviews you like a senior PM, writes a **graded, placeholder-proof PRD, compiles it into a **dependency-ordered task graph, and executes every task with

61010 skills1 agent1 MCP
Get the whole plugin, auto-invoked

Stars on GitHub

Momentum

stars in total
610
Star history of anombyte93/prd-taskmaster 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 prd.

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