From d8bc585bf24dc8b889858bb52666e95a6fb8b7fc Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 29 Jun 2026 23:00:57 -0700 Subject: [PATCH] fix(hooks): resolve worktree-local lefthook instead of path-baking shim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lefthook install bakes an absolute node_modules path into the shared .git/hooks shim. In a fresh worktree that path is stale, so the shim falls through to `mint run csjones/lefthook-plugin` — which collides with Mintlify's `mint` CLI, errors mid-run, and lets lefthook's stage_fixed stash silently revert uncommitted edits. Replace `prepare: lefthook install` with a script that writes hooks resolving each worktree's own node_modules/.bin/lefthook, skipping cleanly (exit 0, no stash, no mint) when deps aren't installed. Co-Authored-By: Claude Opus 4.8 --- package.json | 2 +- scripts/install-hooks.mjs | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 scripts/install-hooks.mjs diff --git a/package.json b/package.json index f12c7cd..f1c0c62 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "lint": "biome check .", "lint:fix": "biome check --fix .", "format": "biome format --write .", - "prepare": "lefthook install", + "prepare": "node ./scripts/install-hooks.mjs", "prepublishOnly": "pnpm run build:clean", "demo:dev": "cd demo && pnpm install && pnpm dev", "demo:build": "cd demo && CI=true pnpm install --frozen-lockfile && pnpm build", diff --git a/scripts/install-hooks.mjs b/scripts/install-hooks.mjs new file mode 100644 index 0000000..9e076e2 --- /dev/null +++ b/scripts/install-hooks.mjs @@ -0,0 +1,44 @@ +#!/usr/bin/env node +// Installs git hooks into the shared common hooks dir (used by all worktrees). +// +// Why not `lefthook install`? Its generated shim bakes an absolute path to +// whichever worktree last ran it, and falls through to `mint run +// csjones/lefthook-plugin` when that path is stale. On a machine where `mint` +// is Mintlify's CLI (not Swift Mint) that branch errors mid-run and lefthook's +// stage_fixed stash can silently revert uncommitted edits in a fresh worktree. +// +// These hooks instead resolve each worktree's OWN node_modules/.bin/lefthook, +// and skip cleanly (exit 0, no stash, no mint) when deps aren't installed. +import { execFileSync } from "node:child_process"; +import { chmodSync, mkdirSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +let hooksDir; +try { + const common = execFileSync("git", ["rev-parse", "--git-common-dir"], { + encoding: "utf8", + }).trim(); + hooksDir = join(common, "hooks"); +} catch { + // Not a git repo (e.g. installed as a published dependency) — nothing to do. + process.exit(0); +} + +const hook = (name) => `#!/bin/sh +# Managed by scripts/install-hooks.mjs — do not edit by hand. +root="$(git rev-parse --show-toplevel)" +bin="$root/node_modules/.bin/lefthook" +if [ ! -x "$bin" ]; then + echo "lefthook: node_modules not installed in this worktree; skipping ${name}." >&2 + echo " run 'pnpm install' here to enable git hooks." >&2 + exit 0 +fi +exec "$bin" run ${name} "$@" +`; + +mkdirSync(hooksDir, { recursive: true }); +for (const name of ["pre-commit", "pre-push"]) { + const file = join(hooksDir, name); + writeFileSync(file, hook(name)); + chmodSync(file, 0o755); +}