Skip to content

feat: add test-runner hook template #20

@mvoutov

Description

@mvoutov

Summary

Add a post-edit-test-runner hook template to the template library (aspens add hook) that automatically runs relevant tests after Claude edits a file.

What it does

A PostToolUse hook that fires after Edit, MultiEdit, or Write tool calls. Instead of running the full test suite (expensive), it maps the edited file to its corresponding test file and runs only that:

  • src/lib/scanner.js → runs tests/scanner.test.js
  • src/services/billing/stripe.ts → runs tests/billing.test.ts or src/services/billing/__tests__/stripe.test.ts
  • app/auth/middleware.py → runs tests/test_auth.py

This gives Claude immediate feedback on breakage mid-session, rather than piling up errors for 20 minutes. The hook is deterministic — Claude can forget instructions, but hooks always fire.

Design considerations

  • Scoped, not full suite — only run the test file that corresponds to the edited file. Full suite on every edit is too expensive for large projects.
  • Framework detection — should detect the test runner from the project (vitest, jest, pytest, go test, etc.) and the test file naming convention (*.test.ts, test_*.py, *_test.go).
  • Configurable patterns — users should be able to customize the file→test mapping (e.g., monorepo structures where tests live in a different package).
  • Timeout — cap test execution at ~30s to avoid blocking Claude on slow test suites.
  • Output to stderr — test results should go to stderr (not stdout) so they don't get injected into Claude's context. Or optionally inject a summary line into context on failure so Claude can self-correct.
  • Graceful skip — if no matching test file is found, exit 0 silently.

Possible implementation

#!/bin/bash
# PostToolUse hook — runs matching test after file edits

tool_info=$(cat)
tool_name=$(echo "$tool_info" | jq -r '.tool_name // empty')
file_path=$(echo "$tool_info" | jq -r '.tool_input.file_path // empty')

# Only fire on edit tools
[[ "$tool_name" =~ ^(Edit|MultiEdit|Write)$ ]] || exit 0
[[ -n "$file_path" ]] || exit 0

# Skip non-source files
[[ "$file_path" =~ \.(test|spec)\. ]] && exit 0  # don't re-run tests on test edits
[[ "$file_path" =~ \.(md|json|yaml|yml)$ ]] && exit 0

# Map source file to test file (detect convention)
# ... framework-specific logic here ...

# Run scoped test with timeout
timeout 30 <runner> <test_file> >&2 2>&1

Template location

src/templates/hooks/post-edit-test-runner.sh — installable via aspens add hook post-edit-test-runner

Prior art

The existing post-tool-use-tracker.sh hook already does file→domain mapping on PostToolUse. This would follow the same pattern but trigger test execution instead of skill tracking.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions