#!/usr/bin/env node
// PreToolUse Bash gate: refuses any command that could kill a human-owned session.
// Fail-closed — an unparseable command, a missing guard module or a thrown error denies.
// There is deliberately no "# raw-ok" escape: the whole point is that no agent can opt out.

import { readFileSync, writeSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

const GUARD = join(dirname(fileURLToPath(import.meta.url)), "..", "lib", "kill-guard.mjs");

function deny(reason) {
  const payload = Buffer.from(
    JSON.stringify({
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: reason,
      },
    }),
  );
  // process.stdout is async on a pipe; process.exit right after truncates a >8 KiB deny.
  for (let done = 0; done < payload.length; ) {
    done += writeSync(1, payload, done, payload.length - done);
  }
}

let raw = "";
try {
  raw = readFileSync(0, "utf8");
} catch {
  process.exit(0);
}
if (!raw) process.exit(0);

let command = "";
try {
  command = JSON.parse(raw)?.tool_input?.command ?? "";
} catch {
  process.exit(0);
}
if (!command || typeof command !== "string") process.exit(0);

try {
  const { evaluate } = await import(GUARD);
  const verdict = evaluate(command);
  if (verdict.deny) deny(verdict.reason);
} catch (err) {
  if (/\bkill\b|cgroup\.kill|pkill|killall/.test(command)) {
    deny(
      `Blocked: the human-session kill guard could not run (${err.message}), so this kill cannot be proven safe.\n` +
        `Fix the guard at ${GUARD} rather than working around it.`,
    );
  }
}
process.exit(0);
