fix(husky): make pre-commit work from GUI git clients (#1740)

GUI clients such as Sourcetree launch git without a full shell PATH, so
npx was not found and the pre-commit hook failed. npm install now writes
~/.config/husky/init.sh (Husky v9 sources it before every hook) using the
node that ran the install, so each developer gets the right path without
committing machine-specific setup. Drop the deprecated husky.sh source
line, which fails in Husky v10.

Also record Rollup's optional platform binaries in package-lock.json so
npm 11 (Node 24) can run npm ci against a lockfile last written by npm 10.

- JIRA: ZAPP-1779
diff --git a/.husky/pre-commit b/.husky/pre-commit
index be6aca7..bf56c04 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,5 +1,4 @@
 #!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
 
 echo "🚀   > Pretty quick reformat..."
 npx pretty-quick --staged
diff --git a/docs/development-instructions.md b/docs/development-instructions.md
index 63f1599..1aa1a3c 100644
--- a/docs/development-instructions.md
+++ b/docs/development-instructions.md
@@ -51,7 +51,17 @@
 
 Zap repo is configured with husky git hooks, that perform some
 pre-commit actions, formatting code, checking the obvious problems and similar. If you properly ran `npm install`, all these hooks should
-be installed. You can make sure they are installed by running `npx husky install`. See `.husky/pre-commit` script to review the actions executed as a pre-commit hook.
+be installed. See `.husky/pre-commit` script to review the actions executed as a pre-commit hook.
+
+**GUI git clients (Sourcetree, Tower, GitHub Desktop, etc.)**
+
+GUI clients launch git without a full shell environment, so `node` and
+`npx` are not on `PATH`. Running `npm install` automatically creates
+`~/.config/husky/init.sh` (via `src-script/setup-husky-env.js`), which
+Husky v9 sources before every hook. No manual setup is required.
+
+If you ever switch Node.js versions, delete the file and re-run
+`npm install` to regenerate it pointing at the new version.
 
 **Format the files:**
 
diff --git a/package-lock.json b/package-lock.json
index cb7db10..10a4b68 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6116,6 +6116,273 @@
         }
       }
     },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz",
+      "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz",
+      "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz",
+      "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz",
+      "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz",
+      "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz",
+      "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz",
+      "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz",
+      "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz",
+      "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz",
+      "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz",
+      "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz",
+      "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz",
+      "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz",
+      "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz",
+      "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.20.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz",
+      "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "peer": true
+    },
     "node_modules/@sideway/address": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
diff --git a/package.json b/package.json
index bda1527..8175a2d 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
     "test:e2e-multiprotocol": "node src-script/zap-uitest.js open multiprotocol",
     "test:e2e-multiprotocol-ci": "node src-script/zap-uitest.js run multiprotocol",
     "report": "node src-script/zap-combine-reports.js",
-    "postinstall": "electron-builder install-app-deps && husky install && npm rebuild canvas --update-binary && npm run version-stamp",
+    "postinstall": "electron-builder install-app-deps && husky install && node src-script/setup-husky-env.js && npm rebuild canvas --update-binary && npm run version-stamp",
     "buildAndPack:win": "npm run build && npm run pack:win",
     "buildAndPack:mac": "npm run build && npm run pack:mac && cp apack.json dist/mac",
     "buildAndPack:linux": "npm run build && npm run pack:linux",
diff --git a/src-script/setup-husky-env.js b/src-script/setup-husky-env.js
new file mode 100644
index 0000000..041b676
--- /dev/null
+++ b/src-script/setup-husky-env.js
@@ -0,0 +1,74 @@
+#!/usr/bin/env node
+/**
+ *
+ *    Copyright (c) 2020 Silicon Labs
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ * Generates ~/.config/husky/init.sh so that GUI git clients (Sourcetree,
+ * Tower, GitHub Desktop, etc.) can find node/npx when running pre-commit
+ * hooks. Husky v9 sources that file automatically before every hook.
+ *
+ * Uses `which node` to locate the stable symlink path of the node binary
+ * (e.g. /opt/homebrew/opt/node@24/bin) rather than process.execPath, which
+ * on Homebrew resolves to a versioned Cellar path that breaks on patch
+ * upgrades. Falls back to process.execPath when `which` is unavailable.
+ *
+ * Skips write when the file already exists so manual customisations are
+ * preserved. Skips entirely in CI environments where hooks do not run.
+ */
+
+'use strict'
+
+const fs = require('fs')
+const path = require('path')
+const os = require('os')
+const { execSync } = require('child_process')
+
+// Husky itself skips hooks in CI; skip this script too.
+if (process.env.CI) {
+  process.exit(0)
+}
+
+const huskyConfigDir = path.join(os.homedir(), '.config', 'husky')
+const initFile = path.join(huskyConfigDir, 'init.sh')
+
+if (fs.existsSync(initFile)) {
+  console.log(`â„šī¸  ~/.config/husky/init.sh already exists — skipping.`)
+  process.exit(0)
+}
+
+// Prefer `which node` (the symlink / stable PATH entry, e.g.
+// /opt/homebrew/opt/node@24/bin) over process.execPath, which on Homebrew
+// resolves to a versioned Cellar path that breaks on patch upgrades.
+// Fall back to process.execPath for environments where `which` is unavailable.
+let nodeDir
+try {
+  const whichNode = execSync('which node', { encoding: 'utf8' }).trim()
+  nodeDir = path.dirname(whichNode)
+} catch (_) {
+  nodeDir = path.dirname(process.execPath)
+}
+
+const content = `# Auto-generated by npm install via src-script/setup-husky-env.js
+# Sourced by Husky v9 before every hook to make node/npx available in
+# GUI git clients that launch git without a full shell environment.
+# Delete this file and re-run "npm install" to regenerate it.
+export PATH="${nodeDir}:$PATH"
+`
+
+fs.mkdirSync(huskyConfigDir, { recursive: true })
+fs.writeFileSync(initFile, content, { mode: 0o644 })
+console.log(`✅  Created ~/.config/husky/init.sh (node dir: ${nodeDir})`)