| name | git-hooks |
| description | Set up, validate, and maintain Git hooks (pre-commit, pre-push, etc.) following best practices. Ensures hooks are version-controlled, automatically installed, and follow consistent patterns across the team. Use when creating new hooks, debugging hook issues, or ensuring hook compliance.
|
| compatibility | Requires Node.js 20+; works with any Git repository |
| metadata | {"version":"2.1","targeting":{"include":[{"repo":"bitsoex/ai-code-instructions"}]}} |
Git Hooks
This skill provides guidance for implementing and maintaining Git hooks that enforce code quality standards before commits and pushes reach the repository.
When to use this skill
- Setting up Git hooks in a new repository
- Creating new pre-commit or pre-push hooks
- Debugging hook installation or execution issues
- Ensuring hooks follow team standards
- Migrating from manual hooks to version-controlled hooks
- Integrating with existing hook systems (Husky, pre-commit, lefthook)
Skill Contents
Sections
Available Resources
📚 references/ - Detailed documentation
📦 assets/ - Templates and resources
Distributed Hooks (Informative Mode)
For repositories receiving distributed AI rules, we provide informative hooks that:
- Never block commits or pushes (always exit 0)
- Warn about issues with clear fix commands
- Auto-detect Node.js via nvm, fnm, or system PATH (shows setup instructions if not found)
- Coexist with existing hook setups (Husky, pre-commit, lefthook)
How It Works
# Source location: global/skills/git-hooks/assets/
# Deployed to target repo as:
.git-hooks/
├── pre-commit → Delegates to hooks-bootstrap.sh (same directory)
├── pre-push → Delegates to hooks-bootstrap.sh (same directory)
├── ensure-node.sh → Ensures Node.js 20+ is available
├── hooks-bootstrap.sh → Entry point, loads Node, runs checks
└── hooks-checks.js → Multi-language quality checks
Enabling Distributed Hooks
git config core.hooksPath .git-hooks
git config --get core.hooksPath
Output Example
============================================================
Bitso Quality Checks (Informative)
============================================================
Pre-commit checks found some issues:
[!] Linting: ESLint errors detected
Run: mise run lint:fix
[!] TypeScript: Type errors detected
Run: npx tsc --noEmit
These are recommendations. Your commit will proceed.
For AI agents: Please address these issues before completing.
============================================================
Coexistence with Existing Hooks
See assets/hooks-bridge-strategy.md for detailed integration patterns with:
- Husky: Add our checks to
.husky/pre-commit
- pre-commit (Python): Add as a
local hook in .pre-commit-config.yaml
- lefthook: Add to
lefthook.yml commands
Assets
| Asset | Purpose |
|---|
assets/ensure-node.sh | Node.js detection and auto-installation |
assets/hooks-bootstrap.sh | Hook entry point (ensures Node, runs checks) |
assets/hooks-checks.js | Multi-language quality checks |
assets/pre-commit | Pre-commit hook entry point |
assets/pre-push | Pre-push hook entry point |
assets/hooks-bridge-strategy.md | Integration patterns for existing setups |
Architecture
Recommended Directory Structure
project/
├── .git-hooks/ # Version-controlled hooks directory
│ ├── pre-commit # Symlink → ../.scripts/pre-commit-hook.sh
│ └── pre-push # Symlink → ../.scripts/pre-push-hook.sh
├── .scripts/
│ ├── setup-hooks.ts # Hook installation script (runs on npm install)
│ ├── pre-commit-hook.sh # Pre-commit hook implementation
│ ├── pre-push-hook.sh # Pre-push hook implementation
│ └── lib/skills/ # Skill modules for hook operations
└── package.json # Contains "prepare": "node .scripts/setup-hooks.ts"
Why This Architecture?
- Version-controlled: Hooks live in
.git-hooks/, tracked by Git
- Automatic installation:
npm install configures hooks via prepare script
- Team consistency: Everyone gets the same hooks automatically
- Implementation separation: Actual logic in
.scripts/, symlinks in .git-hooks/
- Skippable in CI: Setup script detects CI environment and skips
Instructions
Step 1: Create the Hooks Directory
mkdir -p .git-hooks
Step 2: Create the Setup Script
Create .scripts/setup-hooks.ts:
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
if (process.env.CI || process.env.SKIP_HOOKS) {
console.log('⏭️ Skipping hook setup (CI or SKIP_HOOKS=true)');
process.exit(0);
}
const ROOT_DIR = path.join(__dirname, '..');
const HOOKS_DIR = '.git-hooks';
function setupHooks() {
if (!fs.existsSync(path.join(ROOT_DIR, '.git'))) {
console.log('⚠️ Not a git repository, skipping hook setup');
return;
}
const githooksPath = path.join(ROOT_DIR, HOOKS_DIR);
if (!fs.existsSync(githooksPath)) {
console.error(`❌ Hooks directory not found: ${HOOKS_DIR}`);
process.exit(1);
}
try {
execSync(`git config core.hooksPath ${HOOKS_DIR}`, { cwd: ROOT_DIR });
console.log(`✅ Git hooks configured: core.hooksPath → ${HOOKS_DIR}`);
} catch (error) {
const err = error as Error;
console.error('❌ Failed to set core.hooksPath:', err.message);
process.exit(1);
}
}
setupHooks();
Step 3: Configure package.json
Add the prepare script to automatically set up hooks on install:
{
"scripts": {
"prepare": "node .scripts/setup-hooks.ts"
}
}
Step 4: Create Hook Implementation
Create hook scripts in .scripts/ following the template in the References section.
Step 5: Create Symlinks
Create symlinks in .git-hooks/ pointing to the implementation:
cd .git-hooks
ln -sf ../.scripts/pre-commit-hook.sh pre-commit
ln -sf ../.scripts/pre-push-hook.sh pre-push
chmod +x pre-commit pre-push
Step 6: Validate Hook Setup
npm run skills:hooks
node .scripts/skills-cli.ts git-hooks validate
Hook Types
| Hook | When It Runs | Typical Checks |
|---|
pre-commit | Before commit is created | Linting, formatting, tests, validation |
pre-push | Before push to remote | Full test suite, coverage, build verification |
commit-msg | After commit message written | Message format validation |
prepare-commit-msg | Before editor opens | Template insertion |
post-checkout | After checkout completes | Dependency updates, cache clearing |
post-merge | After merge completes | Dependency updates |
Best Practices
1. Exit Codes Matter
if ! npm test; then
echo "Tests failed"
exit 1
fi
2. Provide Clear Feedback
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${YELLOW}🔍 Running tests...${NC}"
if npm test --silent; then
echo -e "${GREEN} ✓ Tests passed${NC}"
else
echo -e "${RED} ✗ Tests failed${NC}"
exit 1
fi
3. Keep Hooks Fast
Pre-commit hooks should complete in seconds, not minutes:
- Run only essential checks
- Use incremental/cached operations where possible
- Move heavy checks to pre-push
4. Allow Emergency Bypass
git commit --no-verify
git push --no-verify
5. Fail Early, Fail Fast
Order checks from fastest to slowest:
echo "Checking for debug statements..."
if grep -r "console.log" src/; then
echo "Remove debug statements before committing"
exit 1
fi
echo "Running linter..."
npm run lint
echo "Running tests..."
npm test
6. Handle Auto-Fixes
If a hook auto-fixes files, stage them:
if git diff --name-only | grep -q "formatted-file.js"; then
git add formatted-file.js
echo "Auto-formatted file added to commit"
fi
7. Never Add Coverage Exclusions as First Approach
IMPORTANT: When pre-push hooks fail due to coverage thresholds, the correct approach is:
- Add tests to increase coverage (preferred)
- Use
--no-verify as a temporary emergency bypass if absolutely necessary
- Never add exclusions to
.c8rc.json, .nycrc, or coverage config
Why?
- Exclusions hide untested code and accumulate over time
- They defeat the purpose of coverage thresholds
- They make it harder to identify actual coverage gaps
Correct approach when coverage fails:
npm run test:coverage:report
npm run test:coverage
git push
Emergency bypass (use sparingly):
git push --no-verify
Never do this:
{
"exclude": [
".scripts/new-module.ts"
]
}
Hook Modes
Git hooks support a unified mode system controlled via environment variables.
Mode Values
| Mode | Description |
|---|
skip | Completely skip the hook - no execution, no output |
info | Show informational messages only; do NOT execute scripts |
warn | Execute scripts, show results, but never fail (always exit 0) |
full | Execute scripts and fail when the script results in a failure (default) |
Environment Variables
| Variable | Description |
|---|
BITSO_MISE_MODE | Global mode for all hooks |
BITSO_MISE_GIT_HOOKS | Category-level mode for all git hooks |
BITSO_MISE_GIT_HOOKS_COMMIT | Hook-specific mode for pre-commit |
BITSO_MISE_GIT_HOOKS_PUSH | Hook-specific mode for pre-push |
BITSO_MISE_GIT_HOOKS_CI | Hook-specific mode for CI validation |
Resolution Order
- Hook-specific env var (e.g.,
BITSO_MISE_GIT_HOOKS_COMMIT)
- Category env var (
BITSO_MISE_GIT_HOOKS)
- Global env var (
BITSO_MISE_MODE)
- Default:
full
Example Configuration
In mise.local.toml:
[env]
BITSO_MISE_GIT_HOOKS = "warn"
BITSO_MISE_GIT_HOOKS_COMMIT = "warn"
BITSO_MISE_GIT_HOOKS_PUSH = "full"
References
Technology-specific hook patterns are available in the references/ folder:
| Technology | Reference |
|---|
| Java | references/java/hook-patterns.md |
| TypeScript/JavaScript | references/typescript/hook-patterns.md |
| Python | references/python/hook-patterns.md |
| Go | references/go/hook-patterns.md |
Documentation
For comprehensive documentation in the repository's docs/ directory, see:
docs/ai-ide-management/concepts/git-hooks-architecture.md - System design and flow diagrams
docs/ai-ide-management/how-tos/enable-git-hooks.md - Setup and troubleshooting
docs/ai-ide-management/concepts/conflict-detection.md - How conflicts are detected during distribution
Related Skills
| Skill | Purpose |
|---|
agent-hooks | AI IDE hooks (Claude Code, Cursor) with enforcing mode |
quality-checks | Quality gate orchestration |
coding-standards | Code style enforcement |
hk (ai-code-instructions only)
Note: This section is specific to the ai-code-instructions repository which uses hk as its git hook manager.
hk provides:
- Parallel execution: Runs multiple linters simultaneously
- Smart stashing: Safely stashes unstaged changes during hooks
- Progress reporting: Clear visual feedback during hook execution
- Profile-based configuration: Enable/disable checks via profiles
Configuration
hk is configured via hk.pkl in the repository root:
hooks {
["pre-commit"] {
stash = "git"
steps {
["eslint-staged"] = new Step { check = "node mise-tasks/check.ts eslint-staged" }
["tests-changed"] = new Step { check = "node mise-tasks/check.ts tests-changed" }
}
}
}
Common Commands
hk run pre-commit
hk run pre-push
hk run ci
hk check
hk check --pr
hk fix
hk fix --pr
hk test
hk validate
git commit --no-verify
HK_SKIP_HOOKS=pre-commit git commit
Installation
brew install hk
pkl download-package --ca-certificates=/path/to/ca.pem \
package://github.com/jdx/hk/releases/download/v1.36.0/hk@1.36.0
hk install
Troubleshooting
Hooks not running
-
Verify hooks are installed:
git config --get core.hooksPath
-
Check symlinks are valid:
ls -la .git-hooks/
-
Verify execute permissions:
chmod +x .git-hooks/*
chmod +x .scripts/*-hook.sh
Hooks running but failing
-
Run hooks manually to see full output:
./.scripts/pre-commit-hook.sh
-
Check for missing dependencies:
npm install
-
Run with DEBUG mode:
DEBUG=1 ./.scripts/pre-commit-hook.sh
Hooks too slow
-
Profile each check:
time npm run lint
time npm test
-
Move slow checks to pre-push
-
Use incremental/cached operations
-
Consider staged-files-only validation
Different behavior locally vs CI
- CI should skip hooks (set
CI=true)
- CI runs validations directly, not via hooks
- Ensure setup-hooks.ts checks for CI environment