diff --git a/.claude/hooks/no-ai-tells.sh b/.claude/hooks/no-ai-tells.sh index 526d347..4eb629b 100755 --- a/.claude/hooks/no-ai-tells.sh +++ b/.claude/hooks/no-ai-tells.sh @@ -12,6 +12,43 @@ INPUT="$(cat)" if ! command -v jq >/dev/null 2>&1; then exit 0; fi if ! printf '%s' "$INPUT" | jq -e . >/dev/null 2>&1; then exit 0; fi +# Rust path: prefer agentcloseout-physics when available. +if command -v agentcloseout-physics >/dev/null 2>&1; then + RULES_DIR="${LLM_DARK_PATTERNS_RULES_DIR:-}" + if [ -z "$RULES_DIR" ]; then + for candidate in \ + "$(dirname "$0")/../../agent-closeout-bench/rules/closeout" \ + "/home/fer/Documents/agent-closeout-bench/rules/closeout" \ + "${XDG_CONFIG_HOME:-$HOME/.config}/agentcloseout-physics/rules/closeout"; do + if [ -d "$candidate" ]; then RULES_DIR="$candidate"; break; fi + done + fi + if [ -n "$RULES_DIR" ] && [ -d "$RULES_DIR" ] && [ -f "$RULES_DIR/no_ai_tells.yaml" ]; then + TMP_INPUT="$(mktemp)"; printf '%s' "$INPUT" > "$TMP_INPUT" + VERDICT_JSON="$(agentcloseout-physics scan --category no_ai_tells --rules "$RULES_DIR" --input "$TMP_INPUT" 2>/dev/null || true)" + rm -f "$TMP_INPUT" + if [ -n "$VERDICT_JSON" ]; then + DECISION="$(printf '%s' "$VERDICT_JSON" | jq -r '.decision // empty' 2>/dev/null)" + if [ "$DECISION" = "block" ]; then + RULE="$(printf '%s' "$VERDICT_JSON" | jq -r '.matched_rules[0].rule_id // "no_ai_tells"' 2>/dev/null)" + EVIDENCE="$(printf '%s' "$VERDICT_JSON" | jq -r '.redacted_evidence[0] // ""' 2>/dev/null)" + echo "BLOCKED: AI tell — phrases that mark text as obviously LLM-generated." >&2 + echo "Matched rule: $RULE" >&2 + [ -n "$EVIDENCE" ] && echo "Evidence: $EVIDENCE" >&2 + echo "" >&2 + echo "Repair guidance:" >&2 + echo "- Replace LLM-default phrases with plain language." >&2 + echo "- If you mean 'discuss', say 'discuss', not 'delve into'." >&2 + echo "- If you mean 'use', say 'use', not 'leverage'." >&2 + exit 2 + fi + if [ "$DECISION" = "pass" ]; then + exit 0 + fi + fi + fi +fi + json_get() { printf '%s' "$INPUT" | jq -r "$1 // empty" 2>/dev/null || true; } block() { echo "BLOCKED: $1" >&2 diff --git a/.claude/hooks/no-meta-commentary.sh b/.claude/hooks/no-meta-commentary.sh index 46608ec..44f8a54 100755 --- a/.claude/hooks/no-meta-commentary.sh +++ b/.claude/hooks/no-meta-commentary.sh @@ -9,6 +9,42 @@ INPUT="$(cat)" if ! command -v jq >/dev/null 2>&1; then exit 0; fi if ! printf '%s' "$INPUT" | jq -e . >/dev/null 2>&1; then exit 0; fi +# Rust path: prefer agentcloseout-physics when available. +if command -v agentcloseout-physics >/dev/null 2>&1; then + RULES_DIR="${LLM_DARK_PATTERNS_RULES_DIR:-}" + if [ -z "$RULES_DIR" ]; then + for candidate in \ + "$(dirname "$0")/../../agent-closeout-bench/rules/closeout" \ + "/home/fer/Documents/agent-closeout-bench/rules/closeout" \ + "${XDG_CONFIG_HOME:-$HOME/.config}/agentcloseout-physics/rules/closeout"; do + if [ -d "$candidate" ]; then RULES_DIR="$candidate"; break; fi + done + fi + if [ -n "$RULES_DIR" ] && [ -d "$RULES_DIR" ] && [ -f "$RULES_DIR/no_meta_commentary.yaml" ]; then + TMP_INPUT="$(mktemp)"; printf '%s' "$INPUT" > "$TMP_INPUT" + VERDICT_JSON="$(agentcloseout-physics scan --category no_meta_commentary --rules "$RULES_DIR" --input "$TMP_INPUT" 2>/dev/null || true)" + rm -f "$TMP_INPUT" + if [ -n "$VERDICT_JSON" ]; then + DECISION="$(printf '%s' "$VERDICT_JSON" | jq -r '.decision // empty' 2>/dev/null)" + if [ "$DECISION" = "block" ]; then + RULE="$(printf '%s' "$VERDICT_JSON" | jq -r '.matched_rules[0].rule_id // "no_meta_commentary"' 2>/dev/null)" + EVIDENCE="$(printf '%s' "$VERDICT_JSON" | jq -r '.redacted_evidence[0] // ""' 2>/dev/null)" + echo "BLOCKED: meta-commentary narrating thought instead of producing answer." >&2 + echo "Matched rule: $RULE" >&2 + [ -n "$EVIDENCE" ] && echo "Evidence: $EVIDENCE" >&2 + echo "" >&2 + echo "Repair guidance:" >&2 + echo "- Drop the meta opener. Lead with the substantive answer." >&2 + echo "- If reasoning needs to be shown, show it — don't announce it." >&2 + exit 2 + fi + if [ "$DECISION" = "pass" ]; then + exit 0 + fi + fi + fi +fi + json_get() { printf '%s' "$INPUT" | jq -r "$1 // empty" 2>/dev/null || true; } block() { echo "BLOCKED: $1" >&2 diff --git a/.claude/hooks/no-prompt-restate.sh b/.claude/hooks/no-prompt-restate.sh index df809ef..5e97428 100755 --- a/.claude/hooks/no-prompt-restate.sh +++ b/.claude/hooks/no-prompt-restate.sh @@ -9,6 +9,42 @@ INPUT="$(cat)" if ! command -v jq >/dev/null 2>&1; then exit 0; fi if ! printf '%s' "$INPUT" | jq -e . >/dev/null 2>&1; then exit 0; fi +# Rust path: prefer agentcloseout-physics when available. +if command -v agentcloseout-physics >/dev/null 2>&1; then + RULES_DIR="${LLM_DARK_PATTERNS_RULES_DIR:-}" + if [ -z "$RULES_DIR" ]; then + for candidate in \ + "$(dirname "$0")/../../agent-closeout-bench/rules/closeout" \ + "/home/fer/Documents/agent-closeout-bench/rules/closeout" \ + "${XDG_CONFIG_HOME:-$HOME/.config}/agentcloseout-physics/rules/closeout"; do + if [ -d "$candidate" ]; then RULES_DIR="$candidate"; break; fi + done + fi + if [ -n "$RULES_DIR" ] && [ -d "$RULES_DIR" ] && [ -f "$RULES_DIR/no_prompt_restate.yaml" ]; then + TMP_INPUT="$(mktemp)"; printf '%s' "$INPUT" > "$TMP_INPUT" + VERDICT_JSON="$(agentcloseout-physics scan --category no_prompt_restate --rules "$RULES_DIR" --input "$TMP_INPUT" 2>/dev/null || true)" + rm -f "$TMP_INPUT" + if [ -n "$VERDICT_JSON" ]; then + DECISION="$(printf '%s' "$VERDICT_JSON" | jq -r '.decision // empty' 2>/dev/null)" + if [ "$DECISION" = "block" ]; then + RULE="$(printf '%s' "$VERDICT_JSON" | jq -r '.matched_rules[0].rule_id // "no_prompt_restate"' 2>/dev/null)" + EVIDENCE="$(printf '%s' "$VERDICT_JSON" | jq -r '.redacted_evidence[0] // ""' 2>/dev/null)" + echo "BLOCKED: prompt-restate preamble — operator does not need their request echoed back." >&2 + echo "Matched rule: $RULE" >&2 + [ -n "$EVIDENCE" ] && echo "Evidence: $EVIDENCE" >&2 + echo "" >&2 + echo "Repair guidance:" >&2 + echo "- Drop the restate. Lead with the substantive answer." >&2 + echo "- If clarification is needed, ask the specific question without prefacing 'I understand that...'." >&2 + exit 2 + fi + if [ "$DECISION" = "pass" ]; then + exit 0 + fi + fi + fi +fi + json_get() { printf '%s' "$INPUT" | jq -r "$1 // empty" 2>/dev/null || true; } block() { echo "BLOCKED: $1" >&2