@hasna/hooks 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -5502,12 +5502,43 @@ function extensionOf(scriptPath) {
5502
5502
  const dot = base.lastIndexOf(".");
5503
5503
  return dot > 0 ? base.slice(dot + 1) : "ts";
5504
5504
  }
5505
+ function shebangInterpreter(firstLine) {
5506
+ if (!firstLine.startsWith("#!"))
5507
+ return null;
5508
+ const rest = firstLine.slice(2).trim();
5509
+ if (!rest)
5510
+ return null;
5511
+ const parts = rest.split(/\s+/);
5512
+ if (parts[0] === "env" || parts[0] === "/usr/bin/env") {
5513
+ return parts[1] && parts[1] !== "-S" ? basename2(parts[1]) : parts[2] ? basename2(parts[2]) : null;
5514
+ }
5515
+ return basename2(parts[0]);
5516
+ }
5517
+ function interpreterFor(name, scriptPath, content) {
5518
+ const ext = extensionOf(scriptPath);
5519
+ const firstLine = content.subarray(0, 512).toString("utf8").split(`
5520
+ `, 1)[0] ?? "";
5521
+ const interp = shebangInterpreter(firstLine);
5522
+ if (interp) {
5523
+ if (interp === "bash" || interp === "sh")
5524
+ return { command: ["/bin/bash"], tempExt: "sh" };
5525
+ if (interp === "node" || interp === "bun")
5526
+ return { command: ["bun", "run"], tempExt: BUN_EXTENSIONS.has(ext) ? ext : "ts" };
5527
+ throw new Error(`Refusing to run hook '${name}': shebang '${firstLine}' is not a recognized interpreter (supported: bash/sh, node/bun)`);
5528
+ }
5529
+ if (BASH_EXTENSIONS.has(ext))
5530
+ return { command: ["/bin/bash"], tempExt: "sh" };
5531
+ if (BUN_EXTENSIONS.has(ext))
5532
+ return { command: ["bun", "run"], tempExt: ext };
5533
+ throw new Error(`Refusing to run hook '${name}': unsupported script extension '.${ext}' (supported: .sh .bash .ts .tsx .js .jsx .mjs .cjs .mts .cts, or a recognized shebang)`);
5534
+ }
5505
5535
  async function executeVerifiedScript(options) {
5506
5536
  const scriptDir = dirname4(options.scriptPath);
5507
- const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes(12).toString("hex")}.${extensionOf(options.scriptPath)}`);
5537
+ const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
5538
+ const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes(12).toString("hex")}.${interpreter.tempExt}`);
5508
5539
  try {
5509
5540
  writeFileSync6(tempPath, options.content, { flag: "wx", mode: 384 });
5510
- const proc = Bun.spawn(["bun", "run", isAbsolute2(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
5541
+ const proc = Bun.spawn([...interpreter.command, isAbsolute2(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
5511
5542
  stdin: new Response(options.stdin),
5512
5543
  stdout: "pipe",
5513
5544
  stderr: "pipe",
@@ -5526,7 +5557,11 @@ async function executeVerifiedScript(options) {
5526
5557
  } catch {}
5527
5558
  }
5528
5559
  }
5529
- var init_run = () => {};
5560
+ var BASH_EXTENSIONS, BUN_EXTENSIONS;
5561
+ var init_run = __esm(() => {
5562
+ BASH_EXTENSIONS = new Set(["sh", "bash"]);
5563
+ BUN_EXTENSIONS = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]);
5564
+ });
5530
5565
 
5531
5566
  // src/lib/custom-install.ts
5532
5567
  var exports_custom_install = {};
package/dist/index.js CHANGED
@@ -4855,12 +4855,43 @@ function extensionOf(scriptPath) {
4855
4855
  const dot = base.lastIndexOf(".");
4856
4856
  return dot > 0 ? base.slice(dot + 1) : "ts";
4857
4857
  }
4858
+ function shebangInterpreter(firstLine) {
4859
+ if (!firstLine.startsWith("#!"))
4860
+ return null;
4861
+ const rest = firstLine.slice(2).trim();
4862
+ if (!rest)
4863
+ return null;
4864
+ const parts = rest.split(/\s+/);
4865
+ if (parts[0] === "env" || parts[0] === "/usr/bin/env") {
4866
+ return parts[1] && parts[1] !== "-S" ? basename2(parts[1]) : parts[2] ? basename2(parts[2]) : null;
4867
+ }
4868
+ return basename2(parts[0]);
4869
+ }
4870
+ function interpreterFor(name, scriptPath, content) {
4871
+ const ext = extensionOf(scriptPath);
4872
+ const firstLine = content.subarray(0, 512).toString("utf8").split(`
4873
+ `, 1)[0] ?? "";
4874
+ const interp = shebangInterpreter(firstLine);
4875
+ if (interp) {
4876
+ if (interp === "bash" || interp === "sh")
4877
+ return { command: ["/bin/bash"], tempExt: "sh" };
4878
+ if (interp === "node" || interp === "bun")
4879
+ return { command: ["bun", "run"], tempExt: BUN_EXTENSIONS.has(ext) ? ext : "ts" };
4880
+ throw new Error(`Refusing to run hook '${name}': shebang '${firstLine}' is not a recognized interpreter (supported: bash/sh, node/bun)`);
4881
+ }
4882
+ if (BASH_EXTENSIONS.has(ext))
4883
+ return { command: ["/bin/bash"], tempExt: "sh" };
4884
+ if (BUN_EXTENSIONS.has(ext))
4885
+ return { command: ["bun", "run"], tempExt: ext };
4886
+ throw new Error(`Refusing to run hook '${name}': unsupported script extension '.${ext}' (supported: .sh .bash .ts .tsx .js .jsx .mjs .cjs .mts .cts, or a recognized shebang)`);
4887
+ }
4858
4888
  async function executeVerifiedScript(options) {
4859
4889
  const scriptDir = dirname4(options.scriptPath);
4860
- const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes(12).toString("hex")}.${extensionOf(options.scriptPath)}`);
4890
+ const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
4891
+ const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes(12).toString("hex")}.${interpreter.tempExt}`);
4861
4892
  try {
4862
4893
  writeFileSync6(tempPath, options.content, { flag: "wx", mode: 384 });
4863
- const proc = Bun.spawn(["bun", "run", isAbsolute2(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
4894
+ const proc = Bun.spawn([...interpreter.command, isAbsolute2(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
4864
4895
  stdin: new Response(options.stdin),
4865
4896
  stdout: "pipe",
4866
4897
  stderr: "pipe",
@@ -4879,7 +4910,11 @@ async function executeVerifiedScript(options) {
4879
4910
  } catch {}
4880
4911
  }
4881
4912
  }
4882
- var init_run = () => {};
4913
+ var BASH_EXTENSIONS, BUN_EXTENSIONS;
4914
+ var init_run = __esm(() => {
4915
+ BASH_EXTENSIONS = new Set(["sh", "bash"]);
4916
+ BUN_EXTENSIONS = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]);
4917
+ });
4883
4918
 
4884
4919
  // src/db/storage-sync.ts
4885
4920
  init_db();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/hooks",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command",
5
5
  "type": "module",
6
6
  "bin": {