Task Execution Hooks
January 8, 2026 ยท View on GitHub
Task hooks allow you to run custom shell commands before and after task execution. This enables automated testing, linting, validation, and other workflow automation.
Overview
Sugar supports two types of hooks per task type:
- Pre-hooks: Execute before the AI agent starts working on the task
- Post-hooks: Execute after the task is completed
Default Hooks
Sugar comes with sensible default hooks for common task types:
| Task Type | Pre-hooks | Post-hooks |
|---|---|---|
bug_fix | - | pytest tests/ -x --tb=short |
feature | - | pytest tests/ -x --tb=short, black --check . |
test | - | pytest tests/ -v |
refactor | - | pytest tests/ -x --tb=short |
style | - | black --check . |
Behavior
Pre-hooks
- Execute in order before task execution begins
- If any pre-hook fails (non-zero exit code), task execution is cancelled
- Task is marked as failed with hook error details
Post-hooks
- Execute in order after task execution completes
- If any post-hook fails, the task is marked as "needs review"
- Task is not automatically failed - allows manual review
- Hook outputs are included in task results
Variable Substitution
Hooks support variable substitution from task data:
{task_id}- Task ID{task_type}- Task type (bug_fix, feature, etc.){task_title}- Task title{task_priority}- Task priority (1-5)
Example:
pre_hooks:
- "echo 'Starting {task_type}: {task_title}'"
Configuration
Set Hooks Programmatically
from sugar.storage.task_type_manager import TaskTypeManager
manager = TaskTypeManager(".sugar/sugar.db")
await manager.initialize()
# Set both pre and post hooks
await manager.set_hooks_for_type(
"bug_fix",
pre_hooks=["echo 'Running pre-checks'"],
post_hooks=[
"pytest tests/ -x",
"black --check .",
"mypy ."
]
)
# Set only post hooks
await manager.set_hooks_for_type(
"feature",
post_hooks=["npm test", "npm run lint"]
)
Get Current Hooks
# Get hooks for a task type
pre_hooks = await manager.get_pre_hooks_for_type("bug_fix")
post_hooks = await manager.get_post_hooks_for_type("bug_fix")
print(f"Pre-hooks: {pre_hooks}")
print(f"Post-hooks: {post_hooks}")
Hook Execution Details
Working Directory
Hooks execute in the project root directory (where Sugar is initialized).
Timeout
Each hook has a default timeout of 300 seconds (5 minutes). Hooks that exceed this timeout are terminated and the task fails.
Output Capture
Both stdout and stderr are captured from hook execution and included in task results for debugging.
Environment
Hooks run in a shell environment with:
- Current working directory set to project root
- Same environment variables as the Sugar process
- Shell features available (pipes, redirects, etc.)
Example Workflows
Python Project
# Bug fixes must pass existing tests
await manager.set_hooks_for_type(
"bug_fix",
post_hooks=[
"pytest tests/ -x",
"black --check .",
"flake8 ."
]
)
# Features require full test coverage
await manager.set_hooks_for_type(
"feature",
post_hooks=[
"pytest tests/ --cov=sugar --cov-fail-under=80",
"black --check .",
"mypy sugar/"
]
)
Node.js Project
await manager.set_hooks_for_type(
"feature",
pre_hooks=["npm install"], # Ensure dependencies
post_hooks=[
"npm test",
"npm run lint",
"npm run type-check"
]
)
Multi-language Project
await manager.set_hooks_for_type(
"bug_fix",
post_hooks=[
# Backend tests
"pytest backend/tests/",
# Frontend tests
"(cd frontend && npm test)",
# Integration tests
"./scripts/integration-tests.sh"
]
)
Disabling Hooks
Hooks can be disabled globally in the executor configuration:
config = {
"hooks_enabled": False, # Disable all hooks
# ... other config
}
executor = AgentSDKExecutor(config)
Or set empty hook arrays:
await manager.set_hooks_for_type(
"bug_fix",
pre_hooks=[],
post_hooks=[]
)
Best Practices
-
Keep hooks fast: Hooks run on every task execution. Keep them under 1-2 minutes.
-
Use selective tests: Instead of running all tests, run only relevant ones:
pytest tests/test_auth.py # Just auth tests pytest tests/ -k "user" # Tests matching "user" -
Fail fast: Use
-xflag with pytest to stop on first failure -
Combine related checks: Group related validation in a single script:
./scripts/quality-checks.sh # Runs multiple checks -
Handle errors gracefully: Pre-hooks should only fail for critical issues. Post-hooks are better for quality checks.
-
Use post-hooks for validation: Post-hooks allow task completion even if validation fails, enabling human review.
Troubleshooting
Hook Timeout
If hooks timeout frequently, increase the timeout in the executor or optimize the hook command:
# In HookExecutor.execute_hooks()
result = await hook_executor.execute_hooks(
hooks,
"post_hooks",
task,
timeout=600 # 10 minutes
)
Hook Failures
Check the task result for hook execution details:
if result.get("post_hook_failed"):
print(f"Failed hook: {result['post_hook_error']}")
print(f"Hook output: {result['post_hook_result']}")
Missing Commands
Ensure commands are available in the shell environment:
# Pre-hook to verify environment
pre_hooks=["which pytest || pip install pytest"]