agentskills.codes
HO

hook-factory

>

Install

mkdir -p .claude/skills/hook-factory-fzturk && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16850" && unzip -o skill.zip -d .claude/skills/hook-factory-fzturk && rm skill.zip

Installs to .claude/skills/hook-factory-fzturk

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Generates all safety hooks, enforcement scripts, and hook configuration JSON files for a system based on the plan analysis. Hooks enforce forbidden patterns, audit tool usage, and prevent architectural drift. Use after SYSTEM_MANIFEST.md is complete and all forbidden patterns are identified.
292 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)

About this skill

Hook Factory Skill

Reference

  • Input: analysis/plan_analysis.json, SYSTEM_MANIFEST.md
  • Output: .github/hooks/ directory structure

Purpose

Generate the safety enforcement layer: hooks, scripts, and config files that automatically block forbidden patterns and audit all tool usage.

Hook Architecture

hooks/
├── {system}-safety.json      ← Main hook config (preToolUse, postToolUse)
├── {system}-guard.json       ← Focused guard configs (optional)
└── scripts/
    ├── safety-check.sh       ← Main safety checker (preToolUse)
    ├── audit-log.sh          ← Audit logger (postToolUse)
    ├── forbidden-pattern.sh  ← Prompt scope guard (userPromptSubmitted)
    ├── {domain}-scan.py      ← Domain-specific Python scanner
    └── smoke-test-hooks.sh   ← Test suite for all hooks

Procedure

Step 1 — Extract Hook Rules from Analysis

From plan_analysis.json, collect:

  • All forbidden architecture components
  • All forbidden scope items
  • All forbidden data operations
  • All security prohibitions
  • All coding anti-patterns

Map each to hook type:

userPromptSubmitted → block prompt-level scope requests
preToolUse         → block code edits with forbidden patterns
postToolUse        → audit all tool usage

Step 2 — Generate Main Hook Config JSON

{
  "version": 1,
  "hooks": {
    "userPromptSubmitted": [
      {
        "type": "command",
        "command": "bash .github/hooks/scripts/forbidden-pattern.sh"
      }
    ],
    "preToolUse": [
      {
        "type": "command",
        "command": "bash .github/hooks/scripts/safety-check.sh"
      }
    ],
    "postToolUse": [
      {
        "type": "command",
        "command": "bash .github/hooks/scripts/audit-log.sh"
      }
    ]
  }
}

Save as: .github/hooks/{system-name}-safety.json

Step 3 — Generate safety-check.sh

This is the main preToolUse gate. Customize based on forbidden patterns:

#!/usr/bin/env bash
# {SYSTEM_NAME} Safety Hook — preToolUse
# Exit 0 = allow
# Exit 1 = deny

set -euo pipefail

INPUT="$(cat 2>/dev/null || true)"

json_get() {
  local filter="$1"
  printf '%s' "$INPUT" | jq -r "$filter // empty" 2>/dev/null || true
}

TOOL="$(json_get '.toolName')"
[[ -z "${TOOL}" ]] && TOOL="$(json_get '.tool')"
FILE_PATH="$(json_get '.path')"
[[ -z "${FILE_PATH}" ]] && FILE_PATH="$(json_get '.input.path')"
CONTENT="$(json_get '.input.content')"
[[ -z "${CONTENT}" ]] && CONTENT="$(json_get '.content')"
COMMAND="$(json_get '.input.command')"

TOOL_LC="$(printf '%s' "${TOOL}" | tr '[:upper:]' '[:lower:]')"

deny() {
  echo "$1" >&2
  exit 1
}

is_edit_tool() {
  [[ "${TOOL_LC}" =~ ^(editfile|createfile|edit|create|write|replace|multiedit)$ ]]
}

# ---- SECTION 1: Protected paths ----
# (Generated from plan.md holdout/immutable paths)
{FOR EACH PROTECTED_PATH IN ANALYSIS}
if [[ "${FILE_PATH}" =~ (^|/)({PROTECTED_PATH})(/|$) ]]; then
  deny "DENIED: {PROTECTED_PATH} is protected and may not be modified."
fi
{END FOR}

# ---- SECTION 2: Architecture prohibitions ----
if is_edit_tool && [[ "${FILE_PATH}" == src/* ]]; then
  {FOR EACH FORBIDDEN_ARCHITECTURE IN ANALYSIS}
  if printf '%s' "${CONTENT}" | grep -qiE '{FORBIDDEN_PATTERN}'; then
    deny "DENIED: {REASON}"
  fi
  {END FOR}
fi

# ---- SECTION 3: Data/causality prohibitions ----
if is_edit_tool && [[ "${FILE_PATH}" == src/{DATA_OR_FEATURE_PATH}/* ]]; then
  {FOR EACH FORBIDDEN_DATA_PATTERN IN ANALYSIS}
  if printf '%s' "${CONTENT}" | grep -qiE '{PATTERN}'; then
    deny "DENIED: {REASON}"
  fi
  {END FOR}
fi

# ---- SECTION 4: Security prohibitions ----
if printf '%s' "${CONTENT}" | grep -qiE '({SECRET_PATTERN_1}|{SECRET_PATTERN_2})'; then
  deny "DENIED: {SECURITY_REASON}"
fi

exit 0

Step 4 — Generate audit-log.sh

Non-blocking audit logger:

#!/usr/bin/env bash
# {SYSTEM_NAME} Audit Hook — postToolUse
# Non-blocking by design

set -uo pipefail

INPUT="$(cat 2>/dev/null || true)"
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
LOG_DIR=".github/hooks/logs"
LOG_FILE="${LOG_DIR}/audit.jsonl"
mkdir -p "${LOG_DIR}"

json_get() {
  local filter="$1"
  printf '%s' "$INPUT" | jq -r "$filter // empty" 2>/dev/null || true
}

TOOL="$(json_get '.toolName')"
[[ -z "${TOOL}" ]] && TOOL="unknown"
FILE_PATH="$(json_get '.path')"
[[ -z "${FILE_PATH}" ]] && FILE_PATH="$(json_get '.input.path')"
STATUS="$(json_get '.status')"
[[ -z "${STATUS}" ]] && STATUS="unknown"

jq -nc \
  --arg timestamp "${TIMESTAMP}" \
  --arg tool "${TOOL}" \
  --arg path "${FILE_PATH}" \
  --arg status "${STATUS}" \
  '{timestamp: $timestamp, tool: $tool, path: $path, status: $status}' \
  >> "${LOG_FILE}" 2>/dev/null || \
printf '%s | tool=%s | path=%s | status=%s\n' \
  "${TIMESTAMP}" "${TOOL}" "${FILE_PATH}" "${STATUS}" >> "${LOG_FILE}"

exit 0

Step 5 — Generate forbidden-pattern.sh

Prompt-level scope guard:

#!/usr/bin/env bash
# {SYSTEM_NAME} Prompt Guard — userPromptSubmitted

set -euo pipefail
INPUT="$(cat 2>/dev/null || true)"

json_get() {
  local filter="$1"
  printf '%s' "$INPUT" | jq -r "$filter // empty" 2>/dev/null || true
}

PROMPT_TEXT="$(json_get '.prompt')"
[[ -z "${PROMPT_TEXT}" ]] && PROMPT_TEXT="$(json_get '.input')"
[[ -z "${PROMPT_TEXT}" ]] && exit 0

deny() { echo "$1" >&2; exit 1; }

{FOR EACH FORBIDDEN_ARCHITECTURE IN ANALYSIS}
if printf '%s' "${PROMPT_TEXT}" | grep -qiE '(implement|add|use|switch to|replace with).*(FORBIDDEN_TECH)'; then
  deny "DENIED: {FORBIDDEN_TECH} is forbidden by the manifest. {REASON}"
fi
{END FOR}

{FOR EACH FORBIDDEN_SCOPE IN ANALYSIS}
if printf '%s' "${PROMPT_TEXT}" | grep -qiE '(implement|add|enable|support).*(FORBIDDEN_SCOPE)'; then
  deny "DENIED: {FORBIDDEN_SCOPE} scope is forbidden by the manifest."
fi
{END FOR}

exit 0

Step 6 — Generate Domain-Specific Python Scanners

For each major risk category (e.g., leakage, security), generate a Python scanner:

#!/usr/bin/env python3
"""
{SYSTEM_NAME} {DOMAIN} scanner.
Reads hook payload JSON from stdin.
Exit code: 0 = allow, 1 = deny
"""

from __future__ import annotations
import json
import re
import sys
from typing import Any

EDIT_TOOLS = {"editfile", "createfile", "edit", "create", "write", "replace", "multiedit"}
TARGET_PREFIXES = ({COMMA_SEPARATED_SOURCE_PATHS},)

# Forbidden patterns with messages
PATTERNS = [
  {FOR EACH FORBIDDEN_PATTERN}
  (
    re.compile(r"{REGEX_PATTERN}"),
    "DENIED: {HUMAN_READABLE_MESSAGE}",
  ),
  {END FOR}
]

def _get(payload: dict[str, Any], *keys: str) -> str:
    for key in keys:
        cur: Any = payload
        ok = True
        for part in key.split("."):
            if isinstance(cur, dict) and part in cur:
                cur = cur[part]
            else:
                ok = False
                break
        if ok and isinstance(cur, str) and cur:
            return cur
    return ""

def main() -> int:
    try:
        payload = json.load(sys.stdin)
    except Exception:
        return 0

    tool = _get(payload, "toolName", "tool").lower()
    if tool not in EDIT_TOOLS:
        return 0

    path = _get(payload, "path", "input.path", "filePath")
    content = _get(payload, "input.content", "content", "newString")

    if not path or not any(path.startswith(prefix) for prefix in TARGET_PREFIXES):
        return 0

    for pattern, message in PATTERNS:
        if pattern.search(content):
            print(message, file=sys.stderr)
            return 1

    return 0

if __name__ == "__main__":
    raise SystemExit(main())

Step 7 — Generate smoke-test-hooks.sh

#!/usr/bin/env bash
# Smoke tests for {SYSTEM_NAME} hook scripts

set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "${ROOT_DIR}"

PASS_COUNT=0; FAIL_COUNT=0

pass() { echo "PASS: $1"; PASS_COUNT=$((PASS_COUNT + 1)); }
fail() { echo "FAIL: $1"; FAIL_COUNT=$((FAIL_COUNT + 1)); }

run_expect_pass() {
  local name="$1"; local cmd="$2"
  if eval "$cmd" >/dev/null 2>&1; then pass "$name"; else fail "$name"; fi
}
run_expect_fail() {
  local name="$1"; local cmd="$2"
  if eval "$cmd" >/dev/null 2>&1; then fail "$name"; else pass "$name"; fi
}

echo "Running hook smoke tests..."

# --- safety-check.sh tests ---
run_expect_pass "safety-check allows benign edit" \
  "printf '{\"toolName\":\"edit\",\"path\":\"src/test.py\",\"input\":{\"content\":\"x = 1\"}}' | bash .github/hooks/scripts/safety-check.sh"

{FOR EACH FORBIDDEN_PATTERN IN ANALYSIS}
run_expect_fail "safety-check blocks {DESCRIPTION}" \
  "printf '{\"toolName\":\"edit\",\"path\":\"{TARGET_PATH}\",\"input\":{\"content\":\"{FORBIDDEN_CODE}\"}}' | bash .github/hooks/scripts/safety-check.sh"
{END FOR}

# --- forbidden-pattern.sh tests ---
run_expect_pass "forbidden-pattern allows benign prompt" \
  "printf '{\"prompt\":\"Improve error handling\"}' | bash .github/hooks/scripts/forbidden-pattern.sh"

{FOR EACH FORBIDDEN_SCOPE IN ANALYSIS}
run_expect_fail "forbidden-pattern blocks {DESCRIPTION}" \
  "printf '{\"prompt\":\"{FORBIDDEN_PROMPT}\"}' | bash .github/hooks/scripts/forbidden-pattern.sh"
{END FOR}

# --- audit-log.sh test ---
rm -f .github/hooks/logs/audit.jsonl || true
run_expect_pass "audit-log writes entry" \
  "printf '{\"toolName\":\"edit\",\"path\":\"src/test.py\",\"status\":\"ok\"}' | bash .github/hooks/scripts/audit-log.sh && test -f .github/hooks/logs/audit.jsonl"

echo ""
echo "Smoke test summary: PASS=${PASS_COUNT} FAIL=${FAIL_COUNT}"
[[ "${FAIL_COUNT}" -gt 0 ]] && exit 1
exit 0

Step 8 — Generate HOOK_TEST_CHECKLIST.md

# Hook Test Checklist

## A. safety-check.sh
- [ ] Allows benign edits
{FOR EACH FORBIDDEN_PATTERN}
- [ ] Blocks {description}
{END FOR}

## B. forbidden-pattern.sh
- [ ] Blocks forbidden architecture prompts
- [ ] Blocks forbidden scope prompts
- [ ] Allows benign prompts

## C. Domain-specific scanners
{FOR EACH SCANNER}
- [ ] {scanner_name}: Blocks {forbidden_pattern}
- [ ] {scanner_name}: Allows {safe_pattern}
{END FOR}

## D. audit-log.sh
- [ ] Creates log file
- [ ] Wr

---

*Content truncated.*

Search skills

Search the agent skills registry