diff --git a/.githooks/README.md b/.githooks/README.md new file mode 100644 index 00000000..9966d666 --- /dev/null +++ b/.githooks/README.md @@ -0,0 +1,42 @@ +# .githooks/ + +These scripts contain the actual logic for Crewli's `post-commit` and +`pre-push` hooks (Claude project-knowledge sync gate, non-blocking +sync-staleness warning). + +## How they're dispatched + +Until WS-3 session 1a (2026-04-29) git invoked these scripts directly +via `git config core.hooksPath .githooks`. From that session onward +they are dispatched by **lefthook**, configured in `lefthook.yml` at +the repo root. Lefthook installs its own wrapper scripts into +`.git/hooks/` and reads `lefthook.yml` to decide what to run; for +this repo the wrapper invokes the same `bash .githooks/` calls +that git used to make. + +The migration is 1:1 by design — the hook implementations live here +because the bash logic (merge-commit handling, conf parsing, +non-blocking warning) is intricate enough that re-translating it +into a YAML `run: |` block would be lossy. + +## Re-installing lefthook + +```bash +pnpm install # postinstall script runs `lefthook install` +``` + +## Emergency rollback + +If lefthook is broken and you need git to invoke these scripts +directly again: + +```bash +git config core.hooksPath .githooks +``` + +To return to lefthook: + +```bash +git config --unset --local core.hooksPath +pnpm exec lefthook install +``` diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 00000000..a8a0fc33 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,31 @@ +# Lefthook configuration. +# +# Replaces the hand-rolled scripts that previously lived under +# .githooks/ and were registered via `git config core.hooksPath +# .githooks`. Behaviour is intentionally 1:1 with those scripts — +# see WS-3 session 1a notes for the migration record. +# +# The .githooks/ scripts remain on disk and contain the actual logic +# (gating against .claude-sync.conf, merge-commit handling, the +# non-blocking pre-push warning). lefthook dispatches to them so the +# delicate gating logic isn't re-translated into YAML. See +# .githooks/README.md for details. + +post-commit: + commands: + sync-claude-docs: + run: bash .githooks/post-commit + +pre-push: + commands: + sync-check: + run: bash .githooks/pre-push + # Manual smoke tests need `lefthook run pre-push --force`: + # without `--force`, lefthook v2 inspects {push_files} (the + # diff between local and remote) and skips when that list is + # empty, which is always the case during a manual run. On a + # real `git push` with commits the file list is non-empty and + # the command fires — matching the legacy hook's "always runs" + # behaviour. (Pushing with zero new commits would be skipped + # under lefthook but is a no-op for the sync-staleness warning + # anyway, so behaviour stays effectively 1:1.) diff --git a/package.json b/package.json index 1c8d7e8d..821277e1 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,10 @@ "private": true, "scripts": { "sync:docs": "bash scripts/sync-claude-docs.sh sync", - "sync:check": "bash scripts/sync-claude-docs.sh check" + "sync:check": "bash scripts/sync-claude-docs.sh check", + "postinstall": "lefthook install" + }, + "devDependencies": { + "lefthook": "^2.1.6" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..67f76922 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,114 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + lefthook: + specifier: ^2.1.6 + version: 2.1.6 + +packages: + + lefthook-darwin-arm64@2.1.6: + resolution: {integrity: sha512-hyB7eeiX78BS66f70byTJacDLC/xV1vgMv9n+idFUsrM7J3Udd/ag9Ag5NP3t0eN0EqQqAtrNnt35EH01lxnRQ==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@2.1.6: + resolution: {integrity: sha512-5Ka6cFxiH83krt+OMRQtmS6zqoZR5SLXSudLjTbZA1c3ZqF0+dqkeb4XcB6plx6WR0GFizabuc6Bi3iXPIe1eQ==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@2.1.6: + resolution: {integrity: sha512-VswyOg5CVN3rMaOJ2HtnkltiMKgFHW/wouWxXsV8RxSa4tgWOKxM0EmSXi8qc2jX+LRga6B0uOY6toXS01zWxA==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@2.1.6: + resolution: {integrity: sha512-vXsCUFYuVwrVWwcypB7Zt2Hf+5pl1V1la7ZfvGYZaTRURu0zF/XUnMF/nOz/PebGv0f4x/iOWXWwP7E42xRWsg==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@2.1.6: + resolution: {integrity: sha512-WDJiQhJdZOvKORZd+kF/ms2l6NSsXzdA9ahflyr65V90AC4jES223W8VtEMbGPUtHuGWMEZ/v/XvwlWv0Ioz9g==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@2.1.6: + resolution: {integrity: sha512-C18nCd7nTX1AVL4TcvwMmLAO1VI1OuGluIOTjiPkBQ746Ls1HhL5rl//jMPACmT28YmxIQJ2ZcLPNmhvEVBZvw==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@2.1.6: + resolution: {integrity: sha512-mZOMxM8HiPxVFXDO3PtCUbH4GB8rkveXhsgXF27oAZTYVzQ3gO9vT6r/pxit6msqRXz3fvcwimLVJgb8eRsa8A==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@2.1.6: + resolution: {integrity: sha512-sG9ALLZSnnMOfXu+B7SmxFhJhuoAh4bqi5En5aaHJET48TqrLOcWWZuH+7ArFM6gr/U5KfSUvdmHFmY8WqCcIg==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@2.1.6: + resolution: {integrity: sha512-lD8yFWY4Csuljd0Rqs7EQaySC0VvDf7V3rN1FhRMUISTRDHutebIom1Loc8ckQPvKYGC6mftT9k0GvipsS+Brw==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@2.1.6: + resolution: {integrity: sha512-q4z2n3xucLscoWiyMwFViEj3N8MDSkPulMwcJYuCYFHoPhP1h+icqNu7QRLGYj6AnVrCQweiUJY3Tb2X+GbD/A==} + cpu: [x64] + os: [win32] + + lefthook@2.1.6: + resolution: {integrity: sha512-w9sBoR0mdN+kJc3SB85VzpiAAl451/rxdCRcZlwW71QLjkeH3EBQFgc4VMj5apePychYDHAlqEWTB8J8JK/j1Q==} + hasBin: true + +snapshots: + + lefthook-darwin-arm64@2.1.6: + optional: true + + lefthook-darwin-x64@2.1.6: + optional: true + + lefthook-freebsd-arm64@2.1.6: + optional: true + + lefthook-freebsd-x64@2.1.6: + optional: true + + lefthook-linux-arm64@2.1.6: + optional: true + + lefthook-linux-x64@2.1.6: + optional: true + + lefthook-openbsd-arm64@2.1.6: + optional: true + + lefthook-openbsd-x64@2.1.6: + optional: true + + lefthook-windows-arm64@2.1.6: + optional: true + + lefthook-windows-x64@2.1.6: + optional: true + + lefthook@2.1.6: + optionalDependencies: + lefthook-darwin-arm64: 2.1.6 + lefthook-darwin-x64: 2.1.6 + lefthook-freebsd-arm64: 2.1.6 + lefthook-freebsd-x64: 2.1.6 + lefthook-linux-arm64: 2.1.6 + lefthook-linux-x64: 2.1.6 + lefthook-openbsd-arm64: 2.1.6 + lefthook-openbsd-x64: 2.1.6 + lefthook-windows-arm64: 2.1.6 + lefthook-windows-x64: 2.1.6