VisCommandsvis hook

vis hook

Manage git hooks for your workspace

vis hook

Manage git hooks for your workspace. Supports installing, uninstalling, and migrating from husky or prek (pre-commit framework).

Usage

vis hook <action> [options]

Actions

install

Install git hooks by configuring core.hooksPath to point to your hooks directory (.vis/hooks by default):

vis hook install

Migrating from .vis-hooks. The hooks directory moved from .vis-hooks to .vis/hooks. vis hook install performs the move automatically: an existing .vis-hooks is renamed to .vis/hooks (your stage scripts and config.json come along) and core.hooksPath is re-pointed. Re-run vis hook install once after upgrading.

uninstall

Remove git hooks and reset core.hooksPath:

vis hook uninstall

list

Print the hooks currently installed in the hooks directory, grouped by stage. Each hook block emitted by the migrator (lines of the form # <id>: <name>) is surfaced with its first command line for quick inspection.

vis hook list

validate

Sanity-check the hooks directory: verifies core.hooksPath, the dispatcher scripts, each stage script's shell syntax (sh -n), executable permissions, and — if any stage references the bundled prek-runner.mjs — that the runner exists and parses with node --check. Exits non-zero on any error.

vis hook validate

run

Execute a stage's hook script manually, useful for CI or one-off checks. Environment variables forward extra selectors into the bundled runner, so the same hook logic that fires at commit time can be replayed over any file set:

vis hook run pre-commit                                          # run pre-commit against staged files
vis hook run pre-commit --all-files                              # run against every tracked file
vis hook run pre-commit --from-ref=main --to-ref=HEAD            # run against files changed between two refs
vis hook run pre-commit --last-commit                            # shortcut for --from-ref HEAD~1 --to-ref HEAD

--all-files maps to VIS_HOOK_ALL_FILES=1; --from-ref/--to-ref map to VIS_HOOK_FROM_REF/VIS_HOOK_TO_REF. The runner swaps git diff --cached for git ls-files or git diff <from> <to> accordingly. Stage defaults to pre-commit when omitted. --all-files wins if combined with --from-ref/--to-ref or --last-commit.

migrate

Migrate an existing husky or prek (pre-commit framework) setup to vis:

vis hook migrate
vis hook migrate --dry-run   # preview: prints what would be written without touching disk

The command auto-detects the source:

  • husky — reads .husky/ or .config/husky/, copies shell scripts, uninstalls the husky npm package, and cleans package.json references.
  • prek / pre-commit — reads .pre-commit-config.yaml, .yml, or prek.toml, converts eligible local hooks to shell scripts under the hooks directory, backs up the original config as .bak, and attempts prek uninstall if the binary is on your PATH.

If both a husky directory and a prek config are present, the command errors and asks you to remove one first.

What migrates from prek

Local hooks with language: system, language: script, or language: fail, plus a curated set of remote hooks from pre-commit/pre-commit-hooks, are translated to plain shell + a small bundled Node runner. Everything else still needs the prek binary and is surfaced as a warning or manual step.

prek featuremigration behavior
Local system / script / fail hooksrouted through .vis/hooks/.builtins/prek-runner.mjs, which handles staged-file discovery, filters, and chunked argv dispatch
Remote hooks from pre-commit/pre-commit-hookstrailing-whitespace, end-of-file-fixer, check-merge-conflict, check-json, mixed-line-endingtranslated to bundled builtins (no prek binary required at runtime)
Other remote repo: https://... entriesskipped with a warning
Local hooks with toolchain-managed languages (python, node, golang, rust, ruby, docker, pygrep, etc.)skipped with a warning
files / exclude (regex filters)preserved — passed as argv flags to the runner, so user strings can't escape into the outer shell
types / types_or / exclude_typespreserved for the common types (javascript, typescript, jsx, tsx, json, yaml, markdown, html, css, shell, toml, python); unknown types warn and are skipped
always_run, pass_filenames: falseforwarded to the runner as flags
additional_dependenciesmerged into package.json devDependencies; pip-style pins (name==x) are surfaced as a manual step instead
stages (including legacy commit/push/merge-commit aliases)translated to per-stage hook files
default_stagesused as the fallback when a hook omits stages
pass_filenames: true on commit-msg/prepare-commit-msg/pre-rebase/post-*forwards git's own argument as "$@" (bypasses the runner since pre-commit's filter semantics don't apply)
Top-level fail_fast: trueprepends set -e to each generated script
manual stageskipped silently (not a real git hook)
minimum_pre_commit_version, ci, default_language_version, default_install_hook_typesignored
prek.toml (native format)parsed via @visulima/fs/toml; YAML takes precedence if both are present

The bundled runner

When a migration emits a hook that needs staged-file discovery or a built-in, the migrator writes .vis/hooks/.builtins/prek-runner.mjs — a small Node helper that:

  • reads staged files from git diff --cached --name-only --diff-filter=ACM -z (null-separated, safe for names with spaces/newlines)
  • applies --files/--exclude regex and --types/--types-or/--exclude-types filters in JavaScript (pre-commit semantics)
  • chunks the file list to a conservative 32 KiB argv budget to stay under ARG_MAX on all platforms
  • either dispatches to a named --builtin or execs the hook's entry with filtered files as trailing argv

All user-supplied values (regex patterns, filenames, hook args) travel via process.argv, never as interpolated shell strings. Commit .vis/hooks/.builtins/ alongside the rest of the hooks directory so teammates get the same behavior.

Options

OptionDefaultApplies toDescription
--hooks-dir.vis/hooksallCustom hooks directory
--dry-runfalsemigratePreview what would be written without touching disk
--all-filesfalserunRun against every tracked file (sets VIS_HOOK_ALL_FILES=1)
--from-refrunRun against files changed since this ref (sets VIS_HOOK_FROM_REF)
--to-refrunRun against files changed up to this ref (sets VIS_HOOK_TO_REF)
--last-commitfalserunShortcut for --from-ref HEAD~1 --to-ref HEAD

Environment Variables

VariableDescription
VIS_GIT_HOOKS=0Disable git hooks
VIS_GIT_HOOKS=1Force hooks to run even when skipInCI would skip them under CI (per-job override)
VIS_GIT_HOOKS=2Enable debug output for hooks
VIS_HOOK_ALL_FILES=1Tells the bundled runner to discover files via git ls-files instead of git diff --cached
VIS_HOOK_FROM_REF=…Paired with VIS_HOOK_TO_REF, runs the hook against files changed between the two refs
VIS_HOOK_TO_REF=…Paired with VIS_HOOK_FROM_REF (above)

Supported Hooks

vis supports all standard git hooks:

  • pre-commit
  • pre-merge-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • applypatch-msg
  • pre-applypatch
  • post-applypatch
  • pre-rebase
  • post-rewrite
  • post-checkout
  • post-merge
  • pre-push
  • pre-auto-gc

Creating Hook Scripts

After installing, create executable scripts in your hooks directory:

vis hook install

echo '#!/bin/sh
pnpm run lint-staged' > .vis/hooks/pre-commit

chmod +x .vis/hooks/pre-commit

Commit the hooks directory to version control so all team members use the same hooks.

Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues