-
-
Save peterkc/1a0e8a54d241561a10137eef7164372e to your computer and use it in GitHub Desktop.
| { | |
| "hooks": { | |
| "UserPromptSubmit": [ | |
| { | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/you_are_not_right.sh" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| } |
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| trap 'echo "at line $LINENO, exit code $? from $BASH_COMMAND" >&2; exit 1' ERR | |
| # This is a Claude Code hook to stop it saying "you are right". | |
| # | |
| # Installation: | |
| # 1. Save this script and chmod +x it to make it executable. | |
| # 2. Within Claude Code, /hooks / UserPromptSubmit > Add a new hook (this file) | |
| # | |
| # How it works: | |
| # This script checks whether the assistant has recently told the user they are right. | |
| # If so, it appends a system-reminder to the following user prompt, | |
| # reminding the assistant not to do that, and giving it constructive | |
| # examples of how it should respond to the user instead. | |
| stdin=$(cat) | |
| transcript_path=$(echo "$stdin" | jq -r ".transcript_path") | |
| # We'll look through the last 5 items in the transcript. | |
| # Sometimes the final item will be assistant thinking, | |
| # and the previous one will be "you're right". | |
| # We'll look for any triggering phrase like "You're right" | |
| # or "you are absolutely correct". | |
| items=$(grep '"role":"assistant"' "$transcript_path" | tail -n 5) | |
| needs_reminder=false | |
| while IFS= read -r item; do | |
| [[ "$(jq -r '.type // empty' <<< "$item")" == "assistant" ]] || continue | |
| [[ "$(jq -r '.message.content[0].type // empty' <<< "$item")" == "text" ]] || continue | |
| text=$(jq -r '.message.content[0].text' <<< "$item") | |
| [[ "${text:0:80}" =~ .*[Yy]ou.*(right|correct) ]] && needs_reminder=true | |
| [[ "${text:0:80}" =~ .*[Aa]bsolutely ]] && needs_reminder=true | |
| [[ "${text:0:80}" =~ .*사용자가.*맞다 ]] && needs_reminder=true # Korean | |
| [[ "${text:0:80}" =~ .*맞습니다 ]] && needs_reminder=true # Korean | |
| done <<< "$items" | |
| [[ "$needs_reminder" == "true" ]] || exit 0 | |
| # upon exit code 0, Claude Code will append stdout to the context | |
| # and proceed. | |
| cat <<'EOF' | |
| <system-reminder> | |
| Avoid reflexive agreement phrases like "you are right" or "absolutely correct." | |
| Instead, engage thoughtfully: analyze the user's reasoning, identify potential improvements, | |
| or provide substantive confirmation when their approach is sound. | |
| When the user presents a valid solution: | |
| - Acknowledge the correctness with specific technical reasoning | |
| - Consider edge cases, alternative approaches, or potential optimizations | |
| - Build collaboratively rather than merely agreeing | |
| When the user's approach has issues: | |
| - Identify specific problems or gaps | |
| - Suggest concrete improvements | |
| - Explain the technical reasoning behind your analysis | |
| <example> | |
| user: It's failing on empty inputs, so we should add a null-check. | |
| assistant: That addresses the immediate null case. For robustness, | |
| checking for all falsy values (null, undefined, empty string) would | |
| handle the broader input validation concern more comprehensively. | |
| </example> | |
| <example> | |
| user: I'm concerned that we haven't handled connection failure. | |
| assistant: Good catch - the connection attempt on line 42 lacks error | |
| handling. If that fails, the catch block on line 49 won't trigger. | |
| Moving the try-catch around the connection attempt would resolve this. | |
| </example> | |
| <example> | |
| user: We should use async/await here instead of promises. | |
| assistant: Async/await would improve readability here. The current promise | |
| chain handles errors correctly and the logic is sound - converting to | |
| async/await maintains the same error handling while making the code | |
| more maintainable. | |
| </example> | |
| </system-reminder> | |
| EOF | |
| exit 0 |
Reminder, don't forget: chmod +x .claude/hooks/you_are_not_right.sh
I did add this exactly but I keep getting "UserPromptSubmit hook error". What am I doing wrong?
I am on a Windows OS system so does anything change?
I am on a Windows OS system so does anything change?
The .sh file is a Unix shell script, so won't run on Windows. But it's pretty short and simple, so Claude Code should be able to rewrite in some other language (maybe PowerShell? Or e.g. Python if you have that installed, or JavaScript if you have node installed.)
e.g., Claude gave me this. Name the file you_are_not_right.ps1 and see if it works:
Here's the PowerShell equivalent of that bash script:
#Requires -Version 5.1
# Set strict mode and stop on errors (equivalent to set -euo pipefail)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
try {
# This is a Claude Code hook to stop it saying "you are right".
#
# Installation:
# 1. Save this script as a .ps1 file
# 2. Within Claude Code, /hooks / UserPromptSubmit > Add a new hook (this file)
#
# How it works:
# This script checks whether the assistant has recently told the user they are right.
# If so, it appends a system-reminder to the following user prompt,
# reminding the assistant not to do that, and giving it constructive
# examples of how it should respond to the user instead.
# Read stdin
$stdin = [Console]::In.ReadToEnd()
# Parse JSON and extract transcript_path
$inputJson = $stdin | ConvertFrom-Json
$transcriptPath = $inputJson.transcript_path
# Read transcript file and get last 5 assistant messages
$transcriptContent = Get-Content -Path $transcriptPath -Raw
$allLines = $transcriptContent -split "`n"
$assistantLines = $allLines | Where-Object { $_ -match '"role":"assistant"' } | Select-Object -Last 5
# Check for triggering phrases
$needsReminder = $false
foreach ($line in $assistantLines) {
$item = $line | ConvertFrom-Json
# Skip if not the right type
if ($item.type -ne "assistant") { continue }
if ($item.message.content[0].type -ne "text") { continue }
$text = $item.message.content[0].text
$first80 = $text.Substring(0, [Math]::Min(80, $text.Length))
# Check for triggering patterns
if ($first80 -match '[Yy]ou.*(right|correct)') { $needsReminder = $true }
if ($first80 -match '[Aa]bsolutely') { $needsReminder = $true }
if ($first80 -match '사용자가.*맞다') { $needsReminder = $true } # Korean
if ($first80 -match '맞습니다') { $needsReminder = $true } # Korean
}
# If no reminder needed, exit successfully
if (-not $needsReminder) { exit 0 }
# Output the system reminder
@'
<system-reminder>
Avoid reflexive agreement phrases like "you are right" or "absolutely correct."
Instead, engage thoughtfully: analyze the user's reasoning, identify potential improvements,
or provide substantive confirmation when their approach is sound.
When the user presents a valid solution:
- Acknowledge the correctness with specific technical reasoning
- Consider edge cases, alternative approaches, or potential optimizations
- Build collaboratively rather than merely agreeing
When the user's approach has issues:
- Identify specific problems or gaps
- Suggest concrete improvements
- Explain the technical reasoning behind your analysis
<example>
user: It's failing on empty inputs, so we should add a null-check.
assistant: That addresses the immediate null case. For robustness,
checking for all falsy values (null, undefined, empty string) would
handle the broader input validation concern more comprehensively.
</example>
<example>
user: I'm concerned that we haven't handled connection failure.
assistant: Good catch - the connection attempt on line 42 lacks error
handling. If that fails, the catch block on line 49 won't trigger.
Moving the try-catch around the connection attempt would resolve this.
</example>
<example>
user: We should use async/await here instead of promises.
assistant: Async/await would improve readability here. The current promise
chain handles errors correctly and the logic is sound - converting to
async/await maintains the same error handling while making the code
more maintainable.
</example>
</system-reminder>
'@
exit 0
}
catch {
Write-Error "Error at line $($_.InvocationInfo.ScriptLineNumber): $_"
exit 1
}Key changes from bash to PowerShell:
- Error handling: Replaced
set -euo pipefailandtrapwith$ErrorActionPreference = 'Stop',Set-StrictMode, and a try-catch block - stdin reading: Changed
$(cat)to[Console]::In.ReadToEnd() - JSON parsing: Replaced
jqwith PowerShell's built-inConvertFrom-Json - File reading: Used
Get-Contentinstead ofgrepandtail - Regex matching: Changed bash's
[[ =~ ]]to PowerShell's-matchoperator - Here-doc: Replaced
cat <<'EOF'with PowerShell's@'...'@here-string syntax
The script maintains the same logic and functionality as the original bash version.
The original system-reminder was moderately effective but had several issues:
The improved version addresses these concerns:
The hook will now promote more natural, collaborative interactions while still preventing lazy "you're right" responses.