From harn
Generate PostToolUse trace logging hook for observability. Triggers: /harn:trace, 'trace logging', 'observable hooks', 'agent trace', 'JSONL logging'
npx claudepluginhub sliday/harnThis skill uses the workspace's default tool permissions.
Generate a PostToolUse hook that logs all tool calls to JSONL for debugging and eval generation.
Provides Ktor server patterns for routing DSL, plugins (auth, CORS, serialization), Koin DI, WebSockets, services, and testApplication testing.
Conducts multi-source web research with firecrawl and exa MCPs: searches, scrapes pages, synthesizes cited reports. For deep dives, competitive analysis, tech evaluations, or due diligence.
Provides demand forecasting, safety stock optimization, replenishment planning, and promotional lift estimation for multi-location retailers managing 300-800 SKUs.
Generate a PostToolUse hook that logs all tool calls to JSONL for debugging and eval generation.
scripts/harness/trace_logger.sh with the following content:#!/usr/bin/env bash
# PostToolUse trace logger — appends JSONL to .claude/agent-trace.jsonl
# Non-blocking observability hook. Always exits 0.
set -euo pipefail
# Harn trace logger — records all tool calls for observability
# Why: https://harn.app/kb/evals.html — "Testing Agent Skills Systematically"
command -v jq &>/dev/null || exit 0
LOG_FILE=".claude/agent-trace.jsonl"
mkdir -p "$(dirname "$LOG_FILE")"
# Rotate log if > 5000 lines
if [ -f "$LOG_FILE" ] && [ "$(wc -l < "$LOG_FILE" 2>/dev/null)" -gt 5000 ]; then
tail -2500 "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"
fi
# Read hook JSON payload from stdin
PAYLOAD="$(cat 2>/dev/null || echo '{}')"
# Extract fields with jq, fallback to defaults
TOOL_NAME="$(echo "$PAYLOAD" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo "unknown")"
EXIT_CODE="$(echo "$PAYLOAD" | jq -r '.exit_code // 0' 2>/dev/null || echo "0")"
ARGS_SUMMARY="$(echo "$PAYLOAD" | jq -r '.tool_input // .parameters // {} | tostring' 2>/dev/null | head -c 200 || echo "{}")"
SESSION_ID="${CLAUDE_SESSION_ID:-unknown}"
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
# Build JSONL entry and append
jq -n -c \
--arg ts "$TIMESTAMP" \
--arg tool "$TOOL_NAME" \
--arg args "$ARGS_SUMMARY" \
--arg exit "$EXIT_CODE" \
--arg sid "$SESSION_ID" \
'{timestamp: $ts, tool_name: $tool, args_summary: $args, exit_code: ($exit | tonumber), session_id: $sid}' \
>> "$LOG_FILE" 2>/dev/null
exit 0
chmod +x scripts/harness/trace_logger.sh
.claude/settings.json. Merge the following into the hooks object (create if absent):{
"hooks": {
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash scripts/harness/trace_logger.sh",
"timeout": 3
}
]
}
]
}
}
session_id comes from the CLAUDE_SESSION_ID environment variable (set automatically by Claude Code); defaults to "unknown"..claude/agent-trace.jsonl — one JSON object per line, suitable for jq queries and eval pipelines.