Skip to main content

Hooks

Execute custom actions before and after agent operations, similar to Windsurf’s Cascade Hooks. Configure hooks via JSON or register Python callables programmatically.

Quick Start

from praisonaiagents.memory import HooksManager

hooks = HooksManager()

# Register a pre-write hook
def before_write(context):
    print(f"About to write to: {context.get('file_path')}")
    # Return modified context or None
    return {"validated": True}

hooks.register("pre_write_code", before_write)

# Execute hooks
result = hooks.execute("pre_write_code", {"file_path": "main.py"})
print(f"Hook success: {result.success}")

Hook Events

EventTriggerUse Case
pre_read_codeBefore reading a fileAccess logging
post_read_codeAfter reading a fileContent validation
pre_write_codeBefore writing to a fileLinting, validation
post_write_codeAfter writing to a fileFormatting, git add
pre_run_commandBefore running a commandSecurity checks
post_run_commandAfter running a commandLogging, cleanup
pre_user_promptBefore processing user inputInput sanitization
post_user_promptAfter processing user inputResponse logging
pre_mcp_tool_useBefore using an MCP toolPermission checks
post_mcp_tool_useAfter using an MCP toolResult logging

Configuration File

Create .praison/hooks.json in your project:
{
  "enabled": true,
  "timeout": 30,
  "hooks": {
    "pre_write_code": "./scripts/lint.sh",
    "post_write_code": [
      "./scripts/format.sh",
      "./scripts/git-add.sh"
    ],
    "pre_run_command": {
      "command": "./scripts/validate-command.sh",
      "timeout": 60,
      "enabled": true,
      "block_on_failure": true,
      "pass_input": true
    }
  }
}

Configuration Options

OptionTypeDefaultDescription
enabledbooleantrueEnable/disable all hooks
timeoutinteger30Global timeout in seconds
hooksobject{}Map of event names to commands

Per-Hook Options

OptionTypeDefaultDescription
commandstringrequiredScript or command to run
timeoutintegerglobalOverride global timeout
enabledbooleantrueEnable/disable this hook
block_on_failurebooleanfalseBlock operation if hook fails
pass_inputbooleantruePass context as environment variables

Python Callable Hooks

Register Python functions as hooks:
from praisonaiagents.memory import HooksManager

hooks = HooksManager()

# Simple hook
def log_writes(context):
    print(f"Writing to {context.get('file_path')}")

hooks.register("pre_write_code", log_writes)

# Hook that modifies context
def add_timestamp(context):
    import time
    context["timestamp"] = time.time()
    return context

hooks.register("pre_write_code", add_timestamp)

# Hook that can block operations
def security_check(context):
    if "password" in context.get("content", "").lower():
        raise ValueError("Cannot write passwords to files!")

hooks.register("pre_write_code", security_check)

# Unregister a hook
hooks.unregister("pre_write_code", log_writes)

Script Hooks

Scripts receive context as environment variables:
#!/bin/bash
# scripts/lint.sh

# Context is passed as environment variables
echo "File: $PRAISON_HOOK_FILE_PATH"
echo "Content length: ${#PRAISON_HOOK_CONTENT}"

# Full context as JSON
echo "Context: $PRAISON_HOOK_CONTEXT"

# Run linting
if ! pylint "$PRAISON_HOOK_FILE_PATH"; then
    echo "Linting failed!"
    exit 1
fi

# Return modified content (optional)
# echo "MODIFIED:new content here"

Environment Variables

VariableDescription
PRAISON_HOOK_FILE_PATHFile path (if applicable)
PRAISON_HOOK_CONTENTContent being written
PRAISON_HOOK_COMMANDCommand being run
PRAISON_HOOK_CONTEXTFull context as JSON

Exit Codes

CodeMeaning
0Success
1Failure (blocks if block_on_failure is true)
OtherFailure

Hook Results

from praisonaiagents.memory import HooksManager, HookResult

hooks = HooksManager()

result = hooks.execute("pre_write_code", {"file_path": "main.py"})

# Check result
print(f"Success: {result.success}")
print(f"Exit code: {result.exit_code}")
print(f"Stdout: {result.stdout}")
print(f"Stderr: {result.stderr}")
print(f"Blocked: {result.blocked}")
print(f"Modified input: {result.modified_input}")

Blocking Operations

Use block_on_failure to prevent operations when hooks fail:
{
  "hooks": {
    "pre_write_code": {
      "command": "./scripts/security-scan.sh",
      "block_on_failure": true
    }
  }
}
result = hooks.execute("pre_write_code", {"content": "..."})

if result.blocked:
    print("Operation blocked by hook!")
    print(f"Reason: {result.stderr}")
else:
    # Proceed with operation
    pass

Statistics

from praisonaiagents.memory import HooksManager

hooks = HooksManager()

# Check if hooks exist for an event
if hooks.has_hooks("pre_write_code"):
    print("Pre-write hooks are configured")

# Get statistics
stats = hooks.get_stats()
print(f"Enabled: {stats['enabled']}")
print(f"Script hooks: {stats['script_hooks']}")
print(f"Callable hooks: {stats['callable_hooks']}")
print(f"Total hooks: {stats['total_hooks']}")
print(f"Events: {stats['events']}")

Best Practices

Hooks run synchronously. Keep them fast (under 5 seconds) to avoid slowing down operations. Use timeout to prevent hanging.
Only block operations for critical checks like security scans. Most hooks should log or modify without blocking.
Hooks should catch exceptions and return meaningful error messages. Don’t let hooks crash the main operation.
Script hooks are good for simple tasks. Use Python callable hooks for complex validation or transformation logic.

See Also