scripts/check_staged checks the current repository's staged selection without
calling a model or committing. It is shared by commit and this
checkout's optional .githooks/pre-commit.
For a quick pre-commit check, use commit --check (see commit --help). The
standalone equivalent is:
scripts/check_stagedIt explicitly reports when nothing was checked because nothing is staged, and
prints a success summary after the configured checks pass. On failure, the final
message reminds you to fix the reported issue, stage the fix, and rerun
commit --check.
It exits successfully when nothing is staged, fails on a failed check or missing required executable, and refuses files that have both staged and unstaged edits. It never stages, unstages, restores, or auto-fixes source files. Resolve partial staging before retrying; the gate does not stash work behind your back.
The gate runs from the target repository's root. Per-file tools receive only staged, existing regular files, not symlinks or their resolved targets:
git diff --cached --checkfor whitespace errors and conflict markers;- shell syntax with Bash, POSIX shell, Ksh or Zsh, plus ShellCheck for non-Zsh sources. Detection uses conventional extensions/startup filenames, shebangs (including interpreter arguments), and ShellCheck dialect directives. ShellCheck follows sourced files in the checkout without executing them; those dependencies may affect results even when they are not staged;
- Ruff lint and formatting when
ruff.toml,.ruff.toml, or a standard unquotedtool.ruffTOML table is present; table whitespace is accepted; - whole-project Pyright when
pyrightconfig.jsonor an unquotedtool.pyrighttable is present. Python deletions, renames out of Python, extensionless Python commands, stub files, and root type-config changes also trigger this check; - Prettier for supported documentation, web, and configuration files when a root
Prettier config or top-level
package.jsonprettierfield exists; - repository-local ESLint for JavaScript/TypeScript, including
.mjs,.cjs,.mtsand.cts, when a recognized root ESLint config file exists; - repository-local whole-project
tsc --noEmitfor TypeScript or root TypeScript config changes, whentsconfig.jsonexists.
Ruff, Pyright, Prettier, ShellCheck and applicable shell interpreters must be
available on the calling process's PATH. ESLint and TypeScript use
node_modules/.bin in the target repository. Node is needed to inspect Prettier
configuration in package.json. No tools are installed or synchronized, and no
package scripts are run.
The deployed checker deliberately does not use the dotfiles development .venv.
Tool versions come from these executables, not uv.lock; use the
locked development commands for pinned
manual verification. The installer provisions ShellCheck on all supported
platforms and global Ruff/Pyright/Prettier unless toolchain setup is skipped.
Existing installations may need those tools prepared separately. IDEs and
non-interactive callers must provide their own appropriate PATH; the hook does
not source interactive shell startup files. A tool installed under an old NVM
Node version or a Homebrew prefix is not necessarily visible to SSH.
git clone brings the hook files, but Git does not activate them automatically.
After a successful installation, install.sh runs scripts/setup_git_hooks for
this checkout. You can do the same after a clone/pull without running the full
installer:
./scripts/setup_git_hooks
commit --helpThe setup command only changes this checkout's local Git configuration. It
preserves any existing core.hooksPath (including inherited configuration) and
any non-sample files in the default hooks directory. Repeated setup is safe; a
preserved custom setup is reported as not activated, not silently replaced.
It does not install tools, run checks, change global Git config, or commit.
To inspect activation or resolve a preserved custom setup manually:
git config --show-origin --get core.hooksPathIf it is unset, also inspect .git/hooks (or the hooks directory reported by
git rev-parse --git-path hooks). Setting core.hooksPath replaces the active
hooks directory; it does not compose existing hooks. After preserving any
existing setup, run from the dotfiles root:
git config --local core.hooksPath .githooksOnce activated, normal git commit, including commits made by agents and
commit_dotfiles, runs the shared checks. After the shared checks, the dotfiles
hook also runs python3 -m unittest tests.test_clear_projects when
scripts/clear_projects, tests/test_clear_projects.py, or
.githooks/pre-commit has staged changes. This focused deletion-safety suite
uses temporary directories, not real projects, and requires python3 on PATH.
It is hook-specific: commit --check alone does not run this suite. The hook
itself does not call a model and does not need MODEL. commit checks on the
host before and after message generation, and an enabled hook checks again when
the host makes the commit. Its model container only writes the message in a
temporary workspace; it never needs to run the repository's hooks. There is no
skip flag passed between these steps.
To undo a newly added setting, use git config --local --unset core.hooksPath.
If you replaced a previous local value, restore that value instead. This hook is
for the dotfiles checkout; do not configure it globally for arbitrary projects.
The standalone check_staged command can still inspect another repository when
invoked there through its absolute path or the dotfiles scripts PATH.
This is root-only configuration discovery, not snapshot isolation. Whole-project
type checks and tool configurations use the current checkout. Unstaged or
untracked dependencies can therefore affect results. Concurrent staging or edits
during checks are not isolated. A Git-selected alternate index (for example,
git commit --only) still comes from Git, not an index assembled by this
script.
Checkers may create their normal caches or build metadata; tsc --noEmit can
still update .tsbuildinfo for incremental/composite projects. Project tool
configuration may execute code, so run checks only in trusted repositories.
Missing configuration is reported for recognized Python/JS/TS/Prettier file families; it is not an error. Other files receive only the Git diff check. The shared checker has no Biome, StyLua, general behavior-test runner, secret scanner, query/N+1 detector, or architectural review. The dotfiles hook's focused cleanup regression suite is the exception described above. A passing gate only means the selected checks passed.
Local hooks are bypassable (--no-verify, changed configuration, or edited hook
code). They are workflow guardrails, not a security boundary. Required CI and
branch protection would be a separate remote enforcement decision.