Skip to main content
In a typical JS project, running a linter before commit requires:
  • husky
  • lint-staged
  • @commitlint/cli
  • @commitlint/config-conventional
  • A .husky directory
  • A prepare script
  • A lint-staged config
wit has hooks built in. One config file.
{
  "hooks": {
    "pre-commit": "npm run lint"
  },
  "staged": {
    "\\.ts$": ["eslint --fix", "prettier --write"]
  }
}
That’s it.

Why wit Hooks Replace Husky

FeatureHuskywit
Installationnpm install huskyBuilt-in
Setupnpx husky installwit hooks setup
Config file.husky/ directory.wit/hooks.json
Lint-stagedSeparate packageBuilt-in staged config
Auto-installRequires prepare scriptwit hooks sync in postinstall

Quick Start

# Set up hooks for your project (like husky install)
wit hooks setup --sample

# Sync hooks after config changes
wit hooks sync

Configuration File

wit uses .wit/hooks.json for hook configuration:
{
  "hooks": {
    "pre-commit": "npm run lint",
    "commit-msg": "npx commitlint --edit $1"
  },
  "staged": {
    "\\.(ts|tsx)$": ["eslint --fix", "prettier --write"],
    "\\.(json|md)$": "prettier --write"
  },
  "enabled": true
}

Config Locations

wit looks for config in these locations (in order):
  1. .wit/hooks.json (recommended)
  2. wit.config.json
  3. package.json under "wit" key

Available Hooks

HookTriggeredCan Abort
pre-commitBefore commit is createdYes
post-commitAfter commit is createdNo
commit-msgAfter commit message enteredYes
pre-pushBefore push to remoteYes
post-mergeAfter merge completesNo
pre-rebaseBefore rebase startsYes
post-checkoutAfter branch switchNo
prepare-commit-msgBefore message editor opensNo

Commands

Setup Hooks

# Initialize hooks for the project
wit hooks setup

# With sample configuration
wit hooks setup --sample

Add Hook Commands

# Add a command to a hook type
wit hooks add pre-commit "npm run lint"
wit hooks add pre-commit "npm test"
wit hooks add commit-msg "npx commitlint --edit \$1"

Sync Configuration

# Apply .wit/hooks.json to actual hook scripts
wit hooks sync

List Hooks

wit hooks
Output:
Hooks Status

Config: .wit/hooks.json
  Run "wit hooks sync" to apply config changes

Installed hooks:

  ● pre-commit
    .wit/hooks/pre-commit
  ● commit-msg
    .wit/hooks/commit-msg

Other Commands

wit hooks install <type>   # Install a hook from template
wit hooks remove <type>    # Remove a hook
wit hooks show <type>      # Show hook content
wit hooks run <type>       # Run a hook manually

Lint-Staged Style Configuration

The staged config runs commands only on staged files matching patterns:
{
  "staged": {
    "\\.(ts|tsx)$": ["eslint --fix", "prettier --write"],
    "\\.(css|scss)$": "stylelint --fix",
    "\\.(json|md)$": "prettier --write"
  }
}

Pattern Syntax

  • Uses JavaScript regex patterns
  • Files are passed as arguments to commands
  • Use {} placeholder to control file position:
{
  "staged": {
    "\\.ts$": "eslint {} --fix"
  }
}

Automatic Setup with npm

Add to your package.json:
{
  "scripts": {
    "postinstall": "wit hooks sync"
  }
}
This ensures all team members get the same hooks after npm install.

Hook Definition Formats

Simple String

{
  "hooks": {
    "pre-commit": "npm run lint"
  }
}

Array of Commands

{
  "hooks": {
    "pre-commit": ["npm run lint", "npm test"]
  }
}

Full Definition Object

{
  "hooks": {
    "pre-commit": {
      "run": "npm run lint",
      "files": ["\\.ts$", "\\.tsx$"],
      "skip": false
    }
  }
}

Writing Custom Hooks

Basic Structure

Hooks are shell scripts in .wit/hooks/:
#!/bin/bash
# .wit/hooks/pre-commit

npm run lint
if [ $? -ne 0 ]; then
    echo "Lint failed. Please fix errors before committing."
    exit 1
fi

exit 0

Pre-commit Hook

Block commits that don’t meet criteria:
#!/bin/bash
# Run linter
npm run lint || exit 1

# Run tests
npm test || exit 1

exit 0

Commit Message Validation

#!/bin/bash
# commit-msg: Validate commit message format

MSG=$(cat "$1")

# Check for conventional commit format
if ! echo "$MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"; then
    echo "Commit message must follow conventional commits format:"
    echo "  feat: add new feature"
    echo "  fix: resolve bug"
    echo "  docs: update documentation"
    exit 1
fi

exit 0

Pre-push Hook

Run tests before pushing:
#!/bin/bash
# pre-push: Run full test suite

npm test
if [ $? -ne 0 ]; then
    echo "Tests must pass before pushing."
    exit 1
fi

exit 0

Bypassing Hooks

Use --no-verify to skip hooks:
wit commit -m "WIP" --no-verify
wit push --no-verify
wit merge feature-branch --no-verify

Hook Environment

Hooks have access to these environment variables:
VariableDescription
$WIT_DIR.wit directory path
$WIT_WORK_DIRRepository root
$WIT_HOOK_TYPECurrent hook type
$WIT_COMMITCommit hash (post-commit)
$WIT_BRANCHCurrent branch name

Migration from Husky

Before (Husky + lint-staged)

// package.json
{
  "devDependencies": {
    "husky": "^8.0.0",
    "lint-staged": "^13.0.0"
  },
  "scripts": {
    "prepare": "husky install"
  },
  "lint-staged": {
    "*.ts": ["eslint --fix", "prettier --write"]
  }
}
# .husky/pre-commit
#!/bin/sh
npx lint-staged

After (wit only)

// .wit/hooks.json
{
  "hooks": {
    "pre-commit": "npm run lint"
  },
  "staged": {
    "\\.ts$": ["eslint --fix", "prettier --write"]
  }
}
// package.json
{
  "scripts": {
    "postinstall": "wit hooks sync"
  }
}
Remove these packages:
  • husky
  • lint-staged
  • @commitlint/cli (use wit’s built-in validation)

Best Practices

Keep hooks fastLong-running hooks slow down your workflow. For expensive operations:
  • Run only on changed files (use staged config)
  • Use background jobs for non-blocking tasks
  • Consider CI for comprehensive checks
Version control your configCommit .wit/hooks.json to share hooks with your team:
wit add .wit/hooks.json
wit commit -m "chore: configure project hooks"
Handle errors gracefullyAlways provide clear error messages:
if [ $? -ne 0 ]; then
    echo "❌ Lint failed. Run 'npm run lint' to see errors."
    exit 1
fi

Troubleshooting

  1. Check if hooks are synced: wit hooks
  2. Run sync: wit hooks sync
  3. Check permissions: ls -la .wit/hooks/
Run wit hooks sync after editing .wit/hooks.json
Use the --no-verify flag:
wit commit -m "WIP" --no-verify
Ensure:
  1. .wit/hooks.json is committed
  2. postinstall script calls wit hooks sync
  3. Team members run npm install after pulling