> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wit.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks

> Pre-commit hooks without the npm package soup

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.

```json theme={null}
{
  "hooks": {
    "pre-commit": "npm run lint"
  },
  "staged": {
    "\\.ts$": ["eslint --fix", "prettier --write"]
  }
}
```

That's it.

## Why wit Hooks Replace Husky

| Feature      | Husky                     | wit                             |
| ------------ | ------------------------- | ------------------------------- |
| Installation | `npm install husky`       | Built-in                        |
| Setup        | `npx husky install`       | `wit hooks setup`               |
| Config file  | `.husky/` directory       | `.wit/hooks.json`               |
| Lint-staged  | Separate package          | Built-in `staged` config        |
| Auto-install | Requires `prepare` script | `wit hooks sync` in postinstall |

## Quick Start

```bash theme={null}
# 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:

```json theme={null}
{
  "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

| Hook                 | Triggered                    | Can Abort |
| -------------------- | ---------------------------- | --------- |
| `pre-commit`         | Before commit is created     | Yes       |
| `post-commit`        | After commit is created      | No        |
| `commit-msg`         | After commit message entered | Yes       |
| `pre-push`           | Before push to remote        | Yes       |
| `post-merge`         | After merge completes        | No        |
| `pre-rebase`         | Before rebase starts         | Yes       |
| `post-checkout`      | After branch switch          | No        |
| `prepare-commit-msg` | Before message editor opens  | No        |

## Commands

### Setup Hooks

```bash theme={null}
# Initialize hooks for the project
wit hooks setup

# With sample configuration
wit hooks setup --sample
```

### Add Hook Commands

```bash theme={null}
# 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

```bash theme={null}
# Apply .wit/hooks.json to actual hook scripts
wit hooks sync
```

### List Hooks

```bash theme={null}
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

```bash theme={null}
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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "staged": {
    "\\.ts$": "eslint {} --fix"
  }
}
```

## Automatic Setup with npm

Add to your `package.json`:

```json theme={null}
{
  "scripts": {
    "postinstall": "wit hooks sync"
  }
}
```

This ensures all team members get the same hooks after `npm install`.

## Hook Definition Formats

### Simple String

```json theme={null}
{
  "hooks": {
    "pre-commit": "npm run lint"
  }
}
```

### Array of Commands

```json theme={null}
{
  "hooks": {
    "pre-commit": ["npm run lint", "npm test"]
  }
}
```

### Full Definition Object

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

## Writing Custom Hooks

### Basic Structure

Hooks are shell scripts in `.wit/hooks/`:

```bash theme={null}
#!/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:

```bash theme={null}
#!/bin/bash
# Run linter
npm run lint || exit 1

# Run tests
npm test || exit 1

exit 0
```

### Commit Message Validation

```bash theme={null}
#!/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:

```bash theme={null}
#!/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:

```bash theme={null}
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:

| Variable         | Description               |
| ---------------- | ------------------------- |
| `$WIT_DIR`       | `.wit` directory path     |
| `$WIT_WORK_DIR`  | Repository root           |
| `$WIT_HOOK_TYPE` | Current hook type         |
| `$WIT_COMMIT`    | Commit hash (post-commit) |
| `$WIT_BRANCH`    | Current branch name       |

## Migration from Husky

### Before (Husky + lint-staged)

```json theme={null}
// package.json
{
  "devDependencies": {
    "husky": "^8.0.0",
    "lint-staged": "^13.0.0"
  },
  "scripts": {
    "prepare": "husky install"
  },
  "lint-staged": {
    "*.ts": ["eslint --fix", "prettier --write"]
  }
}
```

```bash theme={null}
# .husky/pre-commit
#!/bin/sh
npx lint-staged
```

### After (wit only)

```json theme={null}
// .wit/hooks.json
{
  "hooks": {
    "pre-commit": "npm run lint"
  },
  "staged": {
    "\\.ts$": ["eslint --fix", "prettier --write"]
  }
}
```

```json theme={null}
// package.json
{
  "scripts": {
    "postinstall": "wit hooks sync"
  }
}
```

**Remove these packages:**

* husky
* lint-staged
* @commitlint/cli (use wit's built-in validation)

## Best Practices

<Tip>
  **Keep hooks fast**

  Long-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
</Tip>

<Tip>
  **Version control your config**

  Commit `.wit/hooks.json` to share hooks with your team:

  ```bash theme={null}
  wit add .wit/hooks.json
  wit commit -m "chore: configure project hooks"
  ```
</Tip>

<Tip>
  **Handle errors gracefully**

  Always provide clear error messages:

  ```bash theme={null}
  if [ $? -ne 0 ]; then
      echo "❌ Lint failed. Run 'npm run lint' to see errors."
      exit 1
  fi
  ```
</Tip>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Hook not running">
    1. Check if hooks are synced: `wit hooks`
    2. Run sync: `wit hooks sync`
    3. Check permissions: `ls -la .wit/hooks/`
  </Accordion>

  <Accordion title="Config changes not applied">
    Run `wit hooks sync` after editing `.wit/hooks.json`
  </Accordion>

  <Accordion title="Bypassing hooks temporarily">
    Use the `--no-verify` flag:

    ```bash theme={null}
    wit commit -m "WIP" --no-verify
    ```
  </Accordion>

  <Accordion title="Hooks work locally but not for team">
    Ensure:

    1. `.wit/hooks.json` is committed
    2. `postinstall` script calls `wit hooks sync`
    3. Team members run `npm install` after pulling
  </Accordion>
</AccordionGroup>
