Hook feedback to tool result merge pattern. Documents how pre/post hook
feedback (warnings, blocks, suggestions) gets merged into tool results
so the model sees quality signals inline with tool output.
Auto-activates when: writing hooks that produce feedback, reviewing hook output flow.
Triggers: hook feedback, tool result, merge, pre-write, post-edit, quality gate
Installs into .claude/skills of the current project.
Are you the author of Hook Feedback Merge?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/yoodaddy0311-hook-feedback-merge)
---
context: fork
user-invocable: false
name: hook-feedback-merge
description: |
Hook feedback to tool result merge pattern. Documents how pre/post hook
feedback (warnings, blocks, suggestions) gets merged into tool results
so the model sees quality signals inline with tool output.
Auto-activates when: writing hooks that produce feedback, reviewing hook output flow.
Triggers: hook feedback, tool result, merge, pre-write, post-edit, quality gate
lang: [en]
platforms: [claude-code]
level: 2
triggers:
- "hook"
- "feedback"
- "tool result"
- "merge"
- "quality gate"
allowed-tools: [Read, Grep, Glob]
tokens: "~1.5K"
category: "infrastructure"
agents: [devops-engineer, backend-developer]
version: "1.0.0"
source_hash: ba191989
whenNotToUse: "Business logic, UI components, or any code that does not interact with the Claude Code hook system; this skill is exclusively for hook pipeline and tool result flow."
---
# Hook Feedback Merge Pattern
## Overview
Claude Code merges hook stdout JSON directly into tool results. When a hook
emits structured feedback (warnings, blocks, reasons), the model receives it
as part of the tool call response -- not as a separate system message. This
means hook feedback is contextually attached to the exact tool invocation
that triggered it.
## How It Works
```
Tool Call (e.g., Edit)
-> PreToolUse hooks run
-> Hook writes JSON to stdout: { "decision": "block", "reason": "..." }
-> Claude Code merges hook output into tool result
-> Model sees: tool result + hook feedback in one response
```
## Hook Output Contract
Hooks communicate by writing a single JSON object to stdout:
### Allow (passthrough -- no interference)
Print nothing and exit 0. Do not print `{"decision":"approve"}` to allow: the host
reads the legacy `approve` as `permissionDecision: "allow"` and skips the permission
prompt (PreToolUse on the Bash tool, measured on host 2.1.284 with `claude -p`;
evidence: `.artibot/guides/v5-design/evidence/ca04-host-ask-probe.md`, section 3.1).
Other tools and events are unmeasured, so print nothing there as well.
### Block (prevent tool execution)
```json
{
"decision": "block",
"reason": "Human-readable explanation of why blocked"
}
```
### Warn (allow but annotate)
```json
{
"message": "Warning text that appears in tool result"
}
```
### Structured Feedback (rich metadata)
```json
{
"decision": "block",
"reason": "Review gate found 2 issue(s):\n - Bracket mismatch\n - Missing tests",
"issues": ["...", "..."],
"changedFiles": ["src/foo.js"],
"codexCrossCheck": true
}
```
All fields beyond `decision` and `reason`/`message` are passed through and
available to the model as additional context.
## Existing Hooks Using This Pattern
| Hook | Phase | Key Fields |
|------|-------|------------|
| `pre-write-guard.js` | PreToolUse | `decision`, `reason` (block on unread files) |
| `pre-bash.js` | PreToolUse | `decision`, `reason` (block dangerous commands) |
| `quality-gate.js` | PostToolUse | `decision`, `reason` (block); `hookSpecificOutput.additionalContext` (warnings) |
| `stop-review-gate.js` | Stop | `decision`, `reason`, `issues[]`, `changedFiles[]` |
| `pre-compact.js` | PreCompact | `message`, `summary`, `tokenEstimate` |
## Implementation Guidelines
### For Hook Authors
1. **Always use `writeStdout()`** (or `writeJSON()`) from `scripts/utils/index.js`
2. **Use `decision` only to block**: `"block"` plus `reason`. The legacy field takes
just `"approve"` and `"block"`; anything else (`"allow"`, `"warn"`, `"BLOCK"`) is a
schema violation (`CHANGELOG.md` 2.1.1 fixed hooks that emitted `"ALLOW"`/`"BLOCK"`).
Never emit `"approve"` (on PreToolUse it skips the permission prompt); to allow on
any event, print nothing
3. **Include `reason`** with human-readable text -- the model reads this
4. **Add structured data** alongside reason for programmatic consumers
5. **Keep output small** -- hook output adds to the tool result token cost
### For Pre-Phase Hooks (PreToolUse)
```js
// Block pattern
writeStdout({ decision: 'block', reason: 'File not read before write' });
// Allow pattern: write nothing and exit 0. Do NOT writeStdout({ decision: 'approve' })
// -- the host turns it into permissionDecision "allow" and skips the permission prompt.
```
### For Post-Phase Hooks (PostToolUse)
```js
// Warning pattern (non-blocking)
writeStdout({ message: 'console.log detected in written file' });
// Block pattern (rare in post -- use for critical issues)
writeStdout({ decision: 'block', reason: 'Hardcoded secret detected' });
```
### For Stop Hooks
```js
// Review result: block only when there are issues, otherwise write nothing
if (issues.length > 0) {
writeStdout({
decision: 'block',
reason: 'Review gate summary...',
issues,
changedFiles,
});
}
```
## Best Practices
- **Keep `reason` under 500 chars** -- it appears inline in tool results
- **Use `message` for informational output**, `decision`+`reason` for actionable output
- **Never log secrets** in hook output -- it becomes part of the conversation
- **Test hook output** by running the hook manually with `echo '{}' | node hook.js`
- **Use stderr for debug logs** (`process.stderr.write(...)`) -- only stdout is merged
## Anti-Patterns
- Writing free-form text to stdout (must be valid JSON)
- Emitting large payloads (file contents, full diffs) in hook output
- Using `console.log()` in hooks (goes to stdout, corrupts JSON)
- Relying on hook output order (hooks may run in parallel within a phase)
## Rationalizations
> 이 절은 참고 자료다 — 아래 변명·반박은 모델이 작업 중 스스로 지름길을 점검하는 데 쓰고, 사용자에게 묻는 질문 목록이나 별도 게이트로 쓰지 않는다. 반박에 비추어 스스로 바로잡을 수 없으면 이 스킬의 Step·Checkpoint 규칙을 따른다.
The following table captures common excuses agents make to skip the discipline of this skill, paired with factual rebuttals.
| Excuse | Rebuttal |
|--------|----------|
| "hook warnings are advisory, I'll ignore them" | advisory warnings encode lessons from prior failures; ignoring them re-runs the experiment with the known result |
| "merging hook feedback clutters tool results" | unmerged feedback gets lost in stderr noise — merging is how the model actually sees it |
| "the model can't act on hook feedback mid-tool" | the model reads the merged result on the next turn and adjusts; that's exactly the feedback loop you want |
| "pre-hook blocks are too aggressive" | pre-hook blocks catch errors before they cost a tool round-trip; aggressive is the correct default |
| "post-hook suggestions arrive too late" | late suggestions steer the NEXT call; steering is always cheaper than correction |