Skip to content

Instantly share code, notes, and snippets.

@scgreenhalgh
Created January 13, 2026 16:26
Show Gist options
  • Select an option

  • Save scgreenhalgh/59257c031124cea344cb2c60de4f683d to your computer and use it in GitHub Desktop.

Select an option

Save scgreenhalgh/59257c031124cea344cb2c60de4f683d to your computer and use it in GitHub Desktop.
Fix Claude Code's Outdated Solutions Problem Claude Code doesn't know the current date, so it often suggests deprecated APIs, old package versions, and outdated Stack Overflow solutions. This wrapper script fixes that by automatically injecting date context into your CLAUDE.md files. Every time you run `claude`, it updates your global CLAUDE.md …
#!/bin/bash
# setup-claude-date-awareness.sh
#
# Automatically injects current date context into Claude Code's CLAUDE.md files
# so Claude uses up-to-date solutions and documentation.
#
# Tested on: macOS (zsh/bash), Linux (bash/zsh/fish)
#
# Usage:
# chmod +x setup-claude-date-awareness.sh
# ./setup-claude-date-awareness.sh
# source ~/.zshrc # or ~/.bashrc, ~/.config/fish/config.fish
set -e
CLAUDE_DIR="$HOME/.claude"
WRAPPER_SCRIPT="$CLAUDE_DIR/claude-wrapper.sh"
DATEFILE="$CLAUDE_DIR/current_date.txt"
# ─────────────────────────────────────────────────────────────────────────────
# Detect shell and rc file
# ─────────────────────────────────────────────────────────────────────────────
detect_shell() {
local user_shell
user_shell=$(basename "$SHELL")
case "$user_shell" in
zsh)
RC_FILE="${ZDOTDIR:-$HOME}/.zshrc"
SHELL_TYPE="posix"
;;
bash)
if [[ "$OSTYPE" == darwin* ]]; then
RC_FILE="$HOME/.bash_profile"
else
RC_FILE="$HOME/.bashrc"
fi
SHELL_TYPE="posix"
;;
fish)
RC_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/fish/config.fish"
SHELL_TYPE="fish"
;;
ksh|mksh)
RC_FILE="$HOME/.kshrc"
SHELL_TYPE="posix"
;;
*)
RC_FILE="$HOME/.profile"
SHELL_TYPE="posix"
echo "⚠ Unknown shell '$user_shell', using $RC_FILE"
;;
esac
echo "Detected shell: $user_shell"
echo "RC file: $RC_FILE"
}
# ─────────────────────────────────────────────────────────────────────────────
# Create the wrapper script
# ─────────────────────────────────────────────────────────────────────────────
create_wrapper_script() {
mkdir -p "$CLAUDE_DIR"
[[ -f "$WRAPPER_SCRIPT" ]] && cp "$WRAPPER_SCRIPT" "$WRAPPER_SCRIPT.backup.$(date +%s)"
cat > "$WRAPPER_SCRIPT" << 'WRAPPER'
#!/usr/bin/env bash
# Claude Code date-aware wrapper
# Auto-generated by setup-claude-date-awareness.sh
set -e
CLAUDE_DIR="$HOME/.claude"
GLOBAL_MD="$CLAUDE_DIR/CLAUDE.md"
PROJECT_MD="./CLAUDE.md"
DATEFILE="$CLAUDE_DIR/current_date.txt"
WRAPPER_PATH="$CLAUDE_DIR/claude-wrapper.sh"
# ─────────────────────────────────────────────────────────────────────────────
# Portable path resolution (macOS lacks realpath by default)
# ─────────────────────────────────────────────────────────────────────────────
resolve_path() {
local target="$1"
# Try GNU realpath first
if command -v realpath >/dev/null 2>&1; then
realpath "$target" 2>/dev/null && return 0
fi
# Try GNU readlink -f (not available on macOS BSD readlink)
if [[ "$(uname)" != "Darwin" ]] && command -v readlink >/dev/null 2>&1; then
readlink -f "$target" 2>/dev/null && return 0
fi
# macOS/BSD fallback using perl (standard on macOS)
if command -v perl >/dev/null 2>&1; then
perl -e 'use Cwd "abs_path"; print abs_path(shift)' "$target" 2>/dev/null && return 0
fi
# Last resort: manual resolution
if [[ -e "$target" ]]; then
local dir base
dir=$(cd "$(dirname "$target")" 2>/dev/null && pwd)
base=$(basename "$target")
echo "${dir}/${base}"
return 0
fi
# If file doesn't exist, return input unchanged
echo "$target"
}
# ─────────────────────────────────────────────────────────────────────────────
# Get current date (portable across macOS and Linux)
# ─────────────────────────────────────────────────────────────────────────────
current_date=$(date +"%Y-%m-%d")
current_year=$(date +"%Y")
if last_year=$(date -v-1y +"%Y" 2>/dev/null); then
: # macOS BSD date
elif last_year=$(date -d "1 year ago" +"%Y" 2>/dev/null); then
: # GNU date
else
last_year=$((current_year - 1)) # Arithmetic fallback
fi
mkdir -p "$CLAUDE_DIR"
# ─────────────────────────────────────────────────────────────────────────────
# Date block content
# ─────────────────────────────────────────────────────────────────────────────
DATE_BLOCK_CONTENT="## Current Date Context (Auto-Updated)
- Today: ${current_date}
- When searching docs/solutions: prefer ${current_year}-${last_year} sources
- Treat pre-2023 solutions with skepticism unless for stable/legacy APIs"
# ─────────────────────────────────────────────────────────────────────────────
# Update CLAUDE.md - preserves marker position in file
# ─────────────────────────────────────────────────────────────────────────────
update_claude_md() {
local target="$1"
local temp_file
temp_file=$(mktemp)
# Ensure cleanup on any exit from this function
trap "rm -f '$temp_file'" RETURN
if grep -q "<!-- AUTO_DATE -->" "$target" 2>/dev/null; then
# Markers exist: replace content BETWEEN markers, preserving position
# Using environment variable + e modifier for safe multiline handling
DATE_BLOCK_CONTENT="$DATE_BLOCK_CONTENT" perl -0777 -pe '
s/(<!-- AUTO_DATE -->).*?(<!-- \/AUTO_DATE -->)/$1 . "\n" . $ENV{DATE_BLOCK_CONTENT} . "\n" . $2/se
' "$target" > "$temp_file"
# Verify the output is valid
if [[ -s "$temp_file" ]] && grep -q "<!-- AUTO_DATE -->" "$temp_file"; then
mv "$temp_file" "$target"
trap - RETURN # Clear trap since we moved the file
else
echo "Warning: Failed to update $target (perl output invalid)" >&2
return 1
fi
elif [[ "$target" == "$GLOBAL_MD" ]]; then
# Global file without markers: prepend full block with markers
{
echo "<!-- AUTO_DATE -->"
echo "$DATE_BLOCK_CONTENT"
echo "<!-- /AUTO_DATE -->"
echo ""
cat "$target" 2>/dev/null || true
} > "$temp_file"
# Verify and move
if [[ -s "$temp_file" ]]; then
mv "$temp_file" "$target"
trap - RETURN
else
echo "Warning: Failed to create date block for $target" >&2
return 1
fi
fi
# For project files without markers: do nothing (opt-in only)
# temp_file cleanup handled by trap
}
# ─────────────────────────────────────────────────────────────────────────────
# Update date file and CLAUDE.md files
# ─────────────────────────────────────────────────────────────────────────────
# 1. Always update datefile
printf 'Date: %s\nYear: %s\nPrefer sources from: %s-%s\n' \
"$current_date" "$current_year" "$current_year" "$last_year" > "$DATEFILE"
# 2. Update global CLAUDE.md (create if missing)
if [[ -f "$GLOBAL_MD" ]]; then
update_claude_md "$GLOBAL_MD" || true
else
# Create new global file with markers
{
echo "<!-- AUTO_DATE -->"
echo "$DATE_BLOCK_CONTENT"
echo "<!-- /AUTO_DATE -->"
} > "$GLOBAL_MD"
fi
# 3. Update project CLAUDE.md only if opt-in markers exist
if [[ -f "$PROJECT_MD" ]] && grep -q "<!-- AUTO_DATE -->" "$PROJECT_MD" 2>/dev/null; then
update_claude_md "$PROJECT_MD" || true
fi
# ─────────────────────────────────────────────────────────────────────────────
# Find the real claude binary (handles spaces, avoids infinite loop)
# ─────────────────────────────────────────────────────────────────────────────
find_real_claude() {
local wrapper_resolved
wrapper_resolved=$(resolve_path "$WRAPPER_PATH")
# Explicit paths (no globs - handles spaces correctly)
local explicit_paths=(
"$HOME/.claude/local/bin/claude"
"$HOME/.local/bin/claude"
"/usr/local/bin/claude"
"/opt/homebrew/bin/claude"
"/usr/bin/claude"
"/opt/local/bin/claude"
"$HOME/.npm-global/bin/claude"
)
local all_candidates=()
# Add explicit paths
for p in "${explicit_paths[@]}"; do
all_candidates+=("$p")
done
# Add nvm paths using find with null delimiter (handles spaces safely)
if [[ -d "$HOME/.nvm/versions/node" ]]; then
while IFS= read -r -d '' nvm_claude; do
all_candidates+=("$nvm_claude")
done < <(find "$HOME/.nvm/versions/node" -maxdepth 4 -name claude -path "*/bin/claude" -print0 2>/dev/null || true)
fi
# Check all candidates
for candidate in "${all_candidates[@]}"; do
if [[ -x "$candidate" ]]; then
local candidate_resolved
candidate_resolved=$(resolve_path "$candidate")
if [[ "$candidate_resolved" != "$wrapper_resolved" ]]; then
echo "$candidate"
return 0
fi
fi
done
# Search PATH
local IFS=':'
for dir in $PATH; do
[[ -z "$dir" ]] && continue
local candidate="$dir/claude"
if [[ -x "$candidate" ]]; then
local candidate_resolved
candidate_resolved=$(resolve_path "$candidate")
if [[ "$candidate_resolved" != "$wrapper_resolved" ]]; then
echo "$candidate"
return 0
fi
fi
done
return 1
}
# ─────────────────────────────────────────────────────────────────────────────
# Execute real claude
# ─────────────────────────────────────────────────────────────────────────────
REAL_CLAUDE=$(find_real_claude) || {
echo "Error: Could not find claude binary." >&2
echo "Make sure Claude Code is installed: https://docs.anthropic.com/en/docs/claude-code" >&2
exit 1
}
exec "$REAL_CLAUDE" "$@"
WRAPPER
chmod +x "$WRAPPER_SCRIPT"
echo "✓ Created wrapper script: $WRAPPER_SCRIPT"
}
# ─────────────────────────────────────────────────────────────────────────────
# Add shell integration
# ─────────────────────────────────────────────────────────────────────────────
add_shell_integration() {
local marker="# Claude date-aware wrapper"
if grep -qF "$marker" "$RC_FILE" 2>/dev/null; then
echo "⚠ Shell integration already exists in $RC_FILE"
echo " To reinstall, remove the existing claude function first."
return 1
fi
mkdir -p "$(dirname "$RC_FILE")"
touch "$RC_FILE"
cp "$RC_FILE" "$RC_FILE.backup.$(date +%s)"
case "$SHELL_TYPE" in
posix)
cat >> "$RC_FILE" << EOF
$marker
claude() {
"$WRAPPER_SCRIPT" "\$@"
}
EOF
;;
fish)
cat >> "$RC_FILE" << EOF
$marker
function claude
$WRAPPER_SCRIPT \$argv
end
EOF
;;
esac
echo "✓ Added shell integration to $RC_FILE"
}
# ─────────────────────────────────────────────────────────────────────────────
# Backup existing CLAUDE.md
# ─────────────────────────────────────────────────────────────────────────────
backup_claude_md() {
if [[ -f "$CLAUDE_DIR/CLAUDE.md" ]]; then
cp "$CLAUDE_DIR/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md.backup.$(date +%s)"
echo "✓ Backed up existing global CLAUDE.md"
fi
}
# ─────────────────────────────────────────────────────────────────────────────
# Print instructions
# ─────────────────────────────────────────────────────────────────────────────
print_instructions() {
cat << EOF
════════════════════════════════════════════════════════════════════
SETUP COMPLETE
════════════════════════════════════════════════════════════════════
1. Reload your shell config:
source $RC_FILE
Or open a new terminal window.
2. Run claude as normal:
claude
════════════════════════════════════════════════════════════════════
FOR EXISTING PROJECTS WITH CLAUDE.md
════════════════════════════════════════════════════════════════════
Option A — Auto-update (opt-in):
Add these two lines anywhere in your project's CLAUDE.md:
<!-- AUTO_DATE -->
<!-- /AUTO_DATE -->
The date block fills in between markers. Position is preserved.
Option B — Manual check (no file changes):
Add to your project's CLAUDE.md:
## Date Awareness
At session start, run: cat ~/.claude/current_date.txt
════════════════════════════════════════════════════════════════════
FILES
════════════════════════════════════════════════════════════════════
Wrapper: $WRAPPER_SCRIPT
Date file: $DATEFILE
Shell config: $RC_FILE
════════════════════════════════════════════════════════════════════
EOF
}
# ─────────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────────
main() {
echo "Setting up Claude Code date-aware wrapper..."
echo ""
detect_shell
echo ""
backup_claude_md
create_wrapper_script
if add_shell_integration; then
print_instructions
else
echo ""
echo "Wrapper script updated at $WRAPPER_SCRIPT"
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment