Roxabi Boilerplate

Hooks

Guide to Git hooks (Lefthook) and Claude Code hooks configured in Roxabi Boilerplate

Overview

Roxabi Boilerplate uses two layers of hooks: Lefthook for Git hooks (commit-msg, pre-commit, pre-push) and Claude Code hooks for in-editor automation during development.

Git Hooks (Lefthook)

Lefthook manages Git hooks to enforce code quality at three stages of the Git workflow. Configuration lives in lefthook.yml at the repository root.

Hook Stages

StageHookWhat It Does
commit-msgcommitlintValidates commit messages against the Conventional Commits format
pre-commitbiomeRuns biome check --write on staged JS/TS/JSON files, auto-fixes issues, and re-stages the corrected files
pre-pushlintRuns bun run lint (Biome lint on the full repo)
pre-pushtypecheckRuns bun run typecheck (TypeScript strict mode)
pre-pushtestRuns bun run test:coverage (full test suite with coverage)
pre-pushi18nRuns bun run i18n:check (translation completeness)
pre-pushlicenseRuns bun run license:check (dependency license compliance)

The pre-commit and pre-push stages run their commands in parallel for faster execution.

Installation

Lefthook installs automatically when you run bun install thanks to the prepare script in package.json. To install manually:

bunx lefthook install

Skipping Hooks

To bypass hooks for a single operation:

git commit --no-verify
git push --no-verify

Warning: --no-verify skips all hooks for that operation. Use sparingly -- these checks exist to prevent broken code from reaching the remote.

Pre-commit Auto-fix Behavior

The pre-commit Biome hook runs with --write and stage_fixed: true. This means if Biome auto-fixes formatting issues in your staged files, the corrected versions are automatically re-staged. You do not need to manually git add after a formatting fix.

Claude Code Hooks

Hooks configured in .claude/settings.json automate common tasks during Claude Code development sessions.

Biome Auto-Format

Trigger: After Edit or Write operations on TS/JS files

Action: Runs biome check --write to format the file

{
  "matcher": "Edit|Write",
  "hooks": [{
    "type": "command",
    "command": "bunx biome check --write \"$CLAUDE_FILE_PATHS\""
  }]
}

Note: The simplified command above illustrates the hook intent. The actual command in .claude/settings.json adds a case file-type guard (to restrict which extensions trigger Biome), timeout 10s (to prevent hangs on large files), and 2>/dev/null || true (to suppress errors and ensure the hook never blocks the tool operation).

Bun Test Blocker

Trigger: Before any Bash tool invocation (PreToolUse)

Action: Blocks any command that calls bun test without run and redirects to bun run test

Plain bun test invokes Bun's native test runner, which causes a CPU spin in this project due to the Lefthook hook setup. The correct command is bun run test, which delegates to Vitest via TurboRepo — the project's configured test runner. This hook prevents accidental use of the wrong runner during Claude Code development sessions.

Security Check

Trigger: Before Edit or Write operations

Action: Scans for security anti-patterns and warns once per file/rule

{
  "matcher": "Edit|Write",
  "hooks": [{
    "type": "command",
    "command": ".claude/hooks/security-check.js"
  }]
}

Security Patterns Detected

PatternDescription
GitHub Actions InjectionUntrusted input in workflow expressions
Dynamic Code Executioneval() or new Function()
XSS VectorsinnerHTML or dangerouslySetInnerHTML
Hardcoded SecretsAPI keys, passwords in code
SQL InjectionTemplate literal interpolation in queries
Command InjectionTemplate literals in exec/spawn

Behavior

  • Warnings appear once per file per rule per session
  • Warnings don't block operations
  • State is persisted in ~/.claude-security-warnings.json

Adding Custom Hooks

  1. Create hook script in .claude/hooks/
  2. Make it executable: chmod +x script.js
  3. Add to .claude/settings.json

Hook Script Format

#!/usr/bin/env node

const input = JSON.parse(process.env.CLAUDE_TOOL_INPUT || '{}')

// Your logic here

// Output (optional)
console.log(JSON.stringify({
  decision: 'allow', // or 'block'
  message: 'Optional message to display'
}))

Environment Variables

Available in hooks:

VariableDescription
CLAUDE_TOOL_INPUTJSON with tool parameters
CLAUDE_FILE_PATHSAffected file path(s)

We use cookies to improve your experience. You can accept all, reject all, or customize your preferences.