Quality hooks
ward init --quality-hooks installs local hooks for the phase profiles generated by init:
ward init --integration none --gate format,tests --quality-hooks.git/hooks/pre-commitrunsward check --profile pre-commit --format jsonafter the shebang and before existing hook logic. It passes each staged file as--path(added, copied, modified, or renamed; deleted files are left out), so gates with apartialtable check only the commit’s files, andgitleaksscans the staged files instead of the history. When nothing qualifies, as for a commit that only deletes files, every gate runs in full. A file staged in part is checked as it is in the working tree..git/hooks/pre-pushrunsward check --profile pre-push --format jsonafter the shebang and before existing hook logic.- Compilation is intentionally part of
pre-push(andci), notpre-commit; the fast commit hook avoids paying for a compile on every edit. - Hooks report results as JSON on stdout so a coding agent that commits or pushes can read which findings to fix.
- A failed check returns non-zero and blocks the commit or push.
Ward uses marker-bounded sections. Existing user hook content and executable permissions are
preserved. Re-running --quality-hooks refreshes both managed sections without adding duplicates.
To remove only Ward’s quality sections, run:
ward init --remove-quality-hooksThis does not remove unrelated user content, other Ward-managed integrations, or the sections of other projects. If a hook has an incomplete or duplicate Ward quality marker, initialization reports a conflict and leaves the hook unchanged; repair the markers manually before retrying.
Where hooks are installed
Section titled “Where hooks are installed”Ward asks Git which hooks directory it runs (git rev-parse --git-path hooks), so a configured
core.hooksPath and the shared hooks of a linked worktree are honoured. When Git cannot answer,
for example outside a Git work tree, Ward uses .git/hooks in the project.
A hook manager that regenerates its hook files, such as husky or lefthook, may drop Ward’s section
when it rewrites them. Add ward check --profile pre-commit to that manager’s configuration
instead.
Nested projects
Section titled “Nested projects”When the project sits below the repository root, for example apps/api in a monorepo, Ward adds
its sections to the repository’s hooks and never creates .git inside the project. Each section
is keyed by the project path, so several Ward projects can share one hook, and refreshing or
removing one project’s section leaves the others alone:
# ward-owned: quality-hooks v1 begin apps/apiif ! git diff --cached --quiet -- 'apps/api'; then if ! (cd "$(git rev-parse --show-toplevel)"/'apps/api' && ( set -- while IFS= read -r ward_path; do if [ -n "$ward_path" ]; then set -- "$@" "--path=$ward_path"; fi done <<WARD_STAGED$(git -c core.quotePath=false diff --cached --name-only --diff-filter=ACMR --relative)WARD_STAGED ward check --profile pre-commit --format json "$@")); then exit 1 fifi# ward-owned: quality-hooks v1 end apps/apipre-commitruns only when the commit stages a file under the project, and passes the staged paths relative to the project.pre-pushcomparesHEADwith the branch’s upstream and runs only when the project changed. Without an upstream, as on a branch’s first push, it always runs. The section never reads the ref list Git passes on stdin, so hook content after it still receives that list.- A project whose root is the repository root keeps the original unkeyed section, which runs on every commit and push.