Normalize HubSpot pre-hook outputs with machine-readable decision envelopes and human diagnostics.
Not for: Input parsing (use hubspot-hook-input-contracts), shell safety (use hubspot-hook-shell-hardening), or subprocess management.
All HubSpot PreToolUse hooks must emit a single JSON object to stdout:
{
"decision": "block" | "warn" | "allow",
"reason": "<human-readable explanation>",
"tool": "<tool_name>",
"severity": "critical" | "high" | "medium" | "low",
"remediation": "<actionable next step or null>"
}
Stderr is reserved for diagnostic noise only — stack traces, debug logs, verbose API responses. Claude Code reads stdout exclusively for the decision envelope.
| Content Type | Channel | Consumed By |
|---|---|---|
| Decision envelope JSON | stdout | Claude Code hook runtime |
| Human diagnostic message | stdout (inside reason field) | Claude Code display |
| Debug/verbose logs | stderr | Operator terminal only |
| Script error traces | stderr | Operator terminal only |
| Audit log writes | File (logs/) | Log aggregator |
block, warn, or allow.reason must be actionable (e.g., "Bulk delete of 312 contacts exceeds 100-record safety threshold. Use hsdedup with --dry-run first.").process.stdout.write(JSON.stringify(envelope) + '\n') or echo "$envelope" >&1. Never mix diagnostic text with the envelope on stdout.>&2.allow and warn; exit 1 for block (belt-and-suspenders with the JSON decision field).allow (fail-open) and log the anomaly.Use this skill for response envelope design and stdout/stderr channel discipline only.
Defer to hubspot-hook-input-contracts for input parsing and to hubspot-agent-governance-runtime for governance policy.