Skip to content

Instantly share code, notes, and snippets.

@peterkc
Forked from ljw1004/you_are_not_right.sh
Last active November 2, 2025 20:09
Show Gist options
  • Save peterkc/1a0e8a54d241561a10137eef7164372e to your computer and use it in GitHub Desktop.
Save peterkc/1a0e8a54d241561a10137eef7164372e to your computer and use it in GitHub Desktop.
A UserPromptSubmit hook for Claude Code to stop it saying "You're right"
{
"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
@peterkc
Copy link
Author

peterkc commented Aug 5, 2025

The original system-reminder was moderately effective but had several issues:

  • Overly rigid "MUST NEVER" language
  • Forced adversarial stance even when user was correct
  • Limited examples showing only critical analysis

The improved version addresses these concerns:

  1. Balanced Tone: Replaces absolute prohibitions with thoughtful guidance
  2. Collaborative Framework: Encourages building on valid user ideas rather than forced disagreement
  3. Comprehensive Examples: Shows appropriate responses for both flawed and sound user suggestions
  4. Technical Focus: Maintains emphasis on substantive analysis while allowing natural agreement when warranted

The hook will now promote more natural, collaborative interactions while still preventing lazy "you're right" responses.

@peterkc
Copy link
Author

peterkc commented Aug 6, 2025

Reminder, don't forget: chmod +x .claude/hooks/you_are_not_right.sh

@SpeedRanger
Copy link

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?

@wodin
Copy link

wodin commented Nov 2, 2025

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.)

@wodin
Copy link

wodin commented Nov 2, 2025

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:

  1. Error handling: Replaced set -euo pipefail and trap with $ErrorActionPreference = 'Stop', Set-StrictMode, and a try-catch block
  2. stdin reading: Changed $(cat) to [Console]::In.ReadToEnd()
  3. JSON parsing: Replaced jq with PowerShell's built-in ConvertFrom-Json
  4. File reading: Used Get-Content instead of grep and tail
  5. Regex matching: Changed bash's [[ =~ ]] to PowerShell's -match operator
  6. Here-doc: Replaced cat <<'EOF' with PowerShell's @'...'@ here-string syntax

The script maintains the same logic and functionality as the original bash version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment