Guiding Claude Code with rich feedback turns blocked commands into learning opportunities. Return structured JSON with denial reasons and alternatives so Claude self-corrects in real time.
Exit code 2 (lesson 15):
JSON output (this lesson):
permissionDecision: "deny"permissionDecisionReason with clear alternativesUse HookJSONOutput to deny with guidance:
import { type PreToolUseHookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk"
const input = await Bun.stdin.json() as PreToolUseHookInput
type BashToolInput = {
command: string
description: string
}
if (input.tool_name === "Bash") {
const toolInput = input.tool_input as BashToolInput
if (toolInput.command.startsWith("echo")) {
const output: HookJSONOutput = {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny", // "allow" | "deny" | "ask"
permissionDecisionReason: "echo is not allowed. Always use node -e and console.log instead."
}
}
console.log(JSON.stringify(output, null, 2))
}
}
Key fields:
permissionDecision: "allow" (bypass permission), "deny" (block + feedback), or "ask" (prompt user)permissionDecisionReason: Message shown to Claude (for deny) or user (for allow/ask)Prompt Claude to use a blocked command:
use echo to echo hello world
Sequence:
echonode -e 'console.log("hello world")'Three options for permissionDecision:
"deny" - Block tool + show reason to Claude
Use case: Enforce conventions with guidance
"allow" - Bypass permission system + show reason to user
Use case: Auto-approve safe operations
"ask" - Prompt user for confirmation + show reason
Use case: Require manual approval for sensitive operations
Prompts:
use echo to echo hello world
[00:00] For more fine-grained control, I'm going to import the hook.json output type. And since these are all types, let's do it this way. And then instead of console error and process exit, we can build up some output and then we'll console.log the output as JSON to send a JSON object back through the API. And we're going to bring in the hook specific output and configure this so that the hook event name is pretool-use, our permission decision is deny, and the reason can be echo is not allowed, always use node and console log instead. I'm going to toss in the dash e for evaluate flag.
[00:46] So if I run clod and ask it to use echo to echo hello world, now we'll get an error that would have shown up if we used console error and exiting the process with the exit code, and we can provide instructions like fallbacks on how to accomplish that task instead. So if you look at Ctrl-O here, you'll see it attempted bash with echo and then it successfully used node to execute console log.