#!/usr/bin/env ruby
# encoding: UTF-8
# frozen_string_literal: true

# PreToolUse create gate (intent 60b): validate the PROPOSED content of a Write
# to an intent file before it lands. Fires when the target path is an intent file
# inside its own equally-named directory (store/<id>--<slug>/<id>--<slug>.md). It
# validates the proposed content from the hook payload (tool_input.content), NOT
# the on-disk file (which does not exist yet at PreToolUse), using IntentValidator
# for born-complete frontmatter plus the sanctioned section structure.
#
# It depends ONLY on the stdin path + content, never on the auto-bridge or any
# session id, so it runs unconditionally (headless / background sessions).
# It validates ONLY the intent file, never sentinel placeholder lifecycle files
# (spec.md/plan.md/etc.); the path matcher excludes them.
#
# Exit 0 = allow. Exit 2 = block (reason on stderr, shown to the agent).
#
# Reads the Claude Code PreToolUse payload as JSON on STDIN:
#   { "tool_input": { "file_path": "...", "content": "..." } }
# Empty / unparseable / non-matching path => exit 0 (cannot judge, allow).
# Three payload shapes on a matching path (intent 108, D7):
#   Write (content):        validate the proposed content.
#   Edit (old_string):      simulate the replacement on the on-disk file and
#                           validate the RESULT; a missing file blocks.
#   Pathless MCP mutation:  validate the CURRENT on-disk file (the PostToolUse
#                           gate-check backstop validates the result); a
#                           missing file blocks (fail-safe).
#
# Thin CLI wrapper (intent 244): the gate logic itself lives in
# scripts/lib/edit_gates.rb, shared with the merged Claude dispatcher
# (scripts/hook-edit-gates) and, since intent 251, the merged Codex dispatcher
# (scripts/lib/codex_edit_gates.rb). This wrapper has no production caller on
# either harness anymore; it remains as the per-gate isolation surface for the
# hook test suite. Deliberately WITHOUT a top-level rescue, matching this
# script's behavior before the extraction: an internal exception here has
# never been caught.

require "json"
require_relative "lib/edit_gates"

raw = $stdin.read rescue nil
exit 0 if raw.nil? || raw.strip.empty?

payload = JSON.parse(raw) rescue nil
exit 0 unless payload.is_a?(Hash)

outcome = EditGates.create_gate(EditGates.context_from(payload))
exit 0 unless outcome
outcome.lines.each { |line| $stderr.puts line }
exit 2
