Copy-paste harness primitives

Hook Library

Deterministic reflex arcs for Claude Code: block dangerous actions before they run, format and trace edits after they happen, and stop the agent from finishing with broken code.

Quality gates Security guards Runtime hooks Loops

Stop hooks: quality gates

TypeScript gate

Prevents “done” when the repo still has type errors.

Stopincluded in /harn:init
quality_gate.sh
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat /dev/stdin)
IS_ACTIVE=$(echo "$PAYLOAD" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
[ "$IS_ACTIVE" = "true" ] && exit 0
npx tsc --noEmit || exit 2

Python gate

Runs deterministic Python checks before the agent can call the task complete.

Stopruffmypy
quality_gate.sh
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat /dev/stdin)
IS_ACTIVE=$(echo "$PAYLOAD" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
[ "$IS_ACTIVE" = "true" ] && exit 0
ruff check . || exit 2
command -v mypy >/dev/null && mypy . || true

Rust gate

Uses Cargo as the sensor. Broken compilation blocks completion.

Stopcargo
quality_gate.sh
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat /dev/stdin)
IS_ACTIVE=$(echo "$PAYLOAD" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
[ "$IS_ACTIVE" = "true" ] && exit 0
cargo check || exit 2

Go gate

Runs Go's built-in static sensor across the whole module.

Stopgo vet
quality_gate.sh
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat /dev/stdin)
IS_ACTIVE=$(echo "$PAYLOAD" | jq -r '.stop_hook_active // false' 2>/dev/null || echo false)
[ "$IS_ACTIVE" = "true" ] && exit 0
go vet ./... || exit 2

PreToolUse hooks: security guards

Dangerous command blocker

Intercepts destructive Bash patterns before they execute.

PreToolUseBashincluded in /harn:init
security_guard.py
#!/usr/bin/env python3
import json, re, sys
try:
    payload = json.load(sys.stdin)
except (json.JSONDecodeError, EOFError):
    sys.exit(0)
command = payload.get("parameters", {}).get("command", "")
blocked = [r"rm\s+-r[fF]", r"chmod\s+777", r"sudo\s+rm", r"mkfs\.", r"dd\s+if="]
for pattern in blocked:
    if re.search(pattern, command):
        print(f"HARNESS BLOCK: {pattern}", file=sys.stderr)
        sys.exit(2)
sys.exit(0)

Main branch protector

Blocks accidental direct pushes to protected branches.

PreToolUsegit
branch_guard.py
#!/usr/bin/env python3
import json, re, sys
payload = json.load(sys.stdin)
command = payload.get("parameters", {}).get("command", "")
if re.search(r"git\s+push\s+.*\b(main|master)\b", command):
    print("HARNESS BLOCK: create a feat/ or fix/ branch instead of pushing to main.", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

Secret file protector

Stops agents from printing or overwriting likely credential files.

PreToolUsesecrets
secret_guard.py
#!/usr/bin/env python3
import json, re, sys
payload = json.load(sys.stdin)
command = payload.get("parameters", {}).get("command", "")
secret_paths = [r"\.env(\.|\s|$)", r"id_rsa", r"credentials\.json", r"secrets?\.(json|yaml|yml)"]
if any(re.search(p, command) for p in secret_paths):
    print("HARNESS BLOCK: credential-like file access requires explicit human approval.", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

Pipe-to-shell blocker

Denies the most common supply-chain footgun: remote script piped into a shell.

PreToolUsesupply chain
pipe_guard.py
#!/usr/bin/env python3
import json, re, sys
payload = json.load(sys.stdin)
command = payload.get("parameters", {}).get("command", "")
if re.search(r"(?:curl|wget)\b.*\|\s*(?:sudo\s+)?(?:bash|sh)", command):
    print("HARNESS BLOCK: download scripts first, inspect them, then run explicitly.", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

PostToolUse hooks: runtime and observability

Trace logger

Turns invisible agent behavior into an inspectable JSONL trail.

PostToolUseobservabilityincluded in /harn:trace
trace_logger.sh
#!/usr/bin/env bash
set -euo pipefail
mkdir -p .claude
payload=$(cat /dev/stdin)
printf '%s\n' "$payload" >> .claude/agent-trace.jsonl
exit 0

Prettier formatter

Formats JS/TS/CSS/JSON/Markdown immediately after edits.

PostToolUseformatter
auto_format.sh
#!/usr/bin/env bash
set -euo pipefail
payload=$(cat /dev/stdin)
file=$(echo "$payload" | jq -r '.tool_input.file_path // .tool_input.path // empty')
[ -z "$file" ] && exit 0
case "$file" in
  *.js|*.jsx|*.ts|*.tsx|*.json|*.css|*.md) npx prettier --write "$file" >/dev/null 2>&1 || true ;;
esac
exit 0

Checkpoint reminder

Creates a gentle runtime backpressure point for long sessions.

Stopstateful
checkpoint_gate.sh
#!/usr/bin/env bash
set -euo pipefail
if [ ! -f CHECKPOINT.json ]; then
  echo "Harn: long session? Add CHECKPOINT.json before compacting or stopping." >&2
fi
exit 0

MCP health check

Fails gracefully when optional servers are missing instead of derailing the run.

SessionStartMCP
mcp_check.sh
#!/usr/bin/env bash
set -euo pipefail
if [ ! -f .mcp.json ]; then
  exit 0
fi
echo "Harn: MCP config detected — verify required servers before tool-heavy work." >&2
exit 0

Wire hooks in .claude/settings.json

Use this wiring pattern when installing recipes manually. harn's slash commands generate and merge this for you.

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{"type": "command", "command": "python3 scripts/harness/security_guard.py"}]
    }],
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{"type": "command", "command": "bash scripts/harness/trace_logger.sh"}]
    }],
    "Stop": [{
      "matcher": "",
      "hooks": [{"type": "command", "command": "bash scripts/harness/quality_gate.sh"}]
    }]
  }
}

Loops: scheduled prompts for active sessions

/loop is not a hook. Hooks react to lifecycle events like PreToolUse, PostToolUse, and Stop. /loop re-runs a prompt on a schedule while a Claude Code session stays open, so it is the right primitive for polling deploys, CI, PR comments, and long-running checks.

Fixed deploy watcher

Poll a rollout on a known cadence and report only the state change.

/loopactive sessionminimum 1m
Claude Code
/loop 5m check whether the deployment finished. If it failed, summarize the failing step and the next command to run. If it passed, say the deployed URL and stop.

Dynamic CI loop

Let Claude pick the wait time between 1 minute and 1 hour based on whether work is moving.

/loopdynamic intervalCI / PRs
Claude Code
/loop check whether CI passed and whether any PR review comments need action. If there is nothing actionable, choose a longer delay and explain why.

Command loop

Repeat another slash command, like a review pass, without turning it into a custom hook.

/loopslash commandreview
Claude Code
/loop 20m /review-pr 1234

When not to use /loop

Use a hook for immediate enforcement, and use durable scheduling when the task must survive a closed session.

decision rulesession-scoped
cheat sheet
Use hooks for: block unsafe commands, trace edits, enforce quality gates.
Use /loop for: active-session polling and repeated prompts.
Use routines/desktop schedulers/GitHub Actions for: durable unattended jobs.
Remember: scheduled tasks require Claude Code v2.1.72+ and /loop tasks expire after 7 days.