- CLAUDE.md: add "Question the question" and "One clarifying question" rules to Tone and Interaction — XY problem detection, false premise checks, and explicit reframe pattern before answering - Add claude/ detail-file directory (topic docs referenced from CLAUDE.md) - Add ABOUT.md, FUTURE.md - Update memory/, scripts/, settings.yaml with accumulated session changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
162 lines
4.8 KiB
Bash
Executable File
162 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# context-load — Gather project context for a Claude Code session
|
|
#
|
|
# Walks from cwd upward collecting key index files.
|
|
# Outputs structured context to stdout, suitable for --append-system-prompt.
|
|
#
|
|
# What it loads:
|
|
# - Paths to every CLAUDE.md found walking up from cwd (not contents — Claude Code loads those natively)
|
|
# - Paths to every project CLAUDE.md found under each CLAUDE.md directory (for project discovery)
|
|
# - Every CONTEXT.md found walking up from cwd (top-down order)
|
|
# - Every MEMORY.md found walking up from cwd (top-down order)
|
|
# - Every BESTPRACTICES.md found walking up from cwd (top-down order)
|
|
set -uo pipefail
|
|
|
|
# --- Helpers ---
|
|
|
|
emit_header() {
|
|
echo ""
|
|
echo "================================================================"
|
|
echo "=== $1"
|
|
echo "================================================================"
|
|
echo ""
|
|
}
|
|
|
|
emit_file() {
|
|
local filepath="$1"
|
|
emit_header "FILE: $filepath"
|
|
cat "$filepath"
|
|
echo ""
|
|
}
|
|
|
|
# Extract description from ABOUT.md in the same directory as a CLAUDE.md
|
|
get_project_description() {
|
|
local claude_path="$1"
|
|
local dir
|
|
dir="$(dirname "$claude_path")"
|
|
local about="$dir/ABOUT.md"
|
|
if [[ -f "$about" ]]; then
|
|
sed -n 's/^description:[[:space:]]*//p' "$about" | head -1
|
|
fi
|
|
}
|
|
|
|
# --- Discovery: walk from cwd upward ---
|
|
|
|
discover_upward() {
|
|
local dir="$PWD"
|
|
local dirs=()
|
|
while [[ "$dir" != "/" ]]; do
|
|
dirs+=("$dir")
|
|
dir="$(dirname "$dir")"
|
|
done
|
|
# Return in top-down order (reverse — highest ancestor first)
|
|
for ((i=${#dirs[@]}-1; i>=0; i--)); do
|
|
echo "${dirs[$i]}"
|
|
done
|
|
}
|
|
|
|
# Collect dirs that have key index files
|
|
claude_dirs=()
|
|
context_dirs=()
|
|
memory_dirs=()
|
|
bestpractices_dirs=()
|
|
|
|
while IFS= read -r dir; do
|
|
[[ -f "$dir/CLAUDE.md" ]] && claude_dirs+=("$dir")
|
|
[[ -f "$dir/CONTEXT.md" ]] && context_dirs+=("$dir")
|
|
[[ -f "$dir/MEMORY.md" ]] && memory_dirs+=("$dir")
|
|
[[ -f "$dir/BESTPRACTICES.md" ]] && bestpractices_dirs+=("$dir")
|
|
done < <(discover_upward)
|
|
|
|
if [[ ${#claude_dirs[@]} -eq 0 ]]; then
|
|
echo "# No CLAUDE.md found in directory hierarchy" >&2
|
|
echo "# Searched from: $PWD" >&2
|
|
fi
|
|
|
|
# --- Output: Claude profile ---
|
|
|
|
profile_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
profile_name="$(basename "$profile_dir")"
|
|
if [[ "$profile_name" == ".claude" ]]; then
|
|
emit_header "CLAUDE PROFILE: default (~/.claude)"
|
|
else
|
|
emit_header "CLAUDE PROFILE: $profile_name ($profile_dir)"
|
|
fi
|
|
|
|
# --- Output: CLAUDE.md paths (not contents — Claude Code loads those natively) ---
|
|
# Also discover project CLAUDE.md files under each directory for project listing
|
|
|
|
# Track files we've already emitted to avoid duplicates
|
|
declare -A emitted_files
|
|
|
|
emit_header "CLAUDE.md FILES"
|
|
for dir in "${claude_dirs[@]}"; do
|
|
real_path="$(readlink -f "$dir/CLAUDE.md")"
|
|
if [[ -z "${emitted_files[$real_path]:-}" ]]; then
|
|
desc="$(get_project_description "$dir/CLAUDE.md")"
|
|
if [[ -n "$desc" ]]; then
|
|
echo "- $dir/CLAUDE.md — $desc"
|
|
else
|
|
echo "- $dir/CLAUDE.md"
|
|
fi
|
|
emitted_files["$real_path"]=1
|
|
fi
|
|
done
|
|
|
|
# Discover project CLAUDE.md files (depth 2-4 under each CLAUDE.md dir)
|
|
# This replaces the old tree output — gives Claude a fresh project listing
|
|
project_claudes=()
|
|
for dir in "${claude_dirs[@]}"; do
|
|
while IFS= read -r f; do
|
|
real_path="$(readlink -f "$f")"
|
|
if [[ -z "${emitted_files[$real_path]:-}" ]]; then
|
|
project_claudes+=("$f")
|
|
emitted_files["$real_path"]=1
|
|
fi
|
|
done < <(find "$dir" -mindepth 2 -maxdepth 4 -name CLAUDE.md -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/__pycache__/*' 2>/dev/null | sort)
|
|
done
|
|
|
|
if [[ ${#project_claudes[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo "Project CLAUDE.md files:"
|
|
for f in "${project_claudes[@]}"; do
|
|
desc="$(get_project_description "$f")"
|
|
if [[ -n "$desc" ]]; then
|
|
echo "- $f — $desc"
|
|
else
|
|
echo "- $f"
|
|
fi
|
|
done
|
|
fi
|
|
echo ""
|
|
|
|
# --- Output: CONTEXT.md files (top-down) ---
|
|
|
|
for dir in "${context_dirs[@]}"; do
|
|
real_path="$(readlink -f "$dir/CONTEXT.md")"
|
|
if [[ -z "${emitted_files[$real_path]:-}" ]]; then
|
|
emit_file "$dir/CONTEXT.md"
|
|
emitted_files["$real_path"]=1
|
|
fi
|
|
done
|
|
|
|
# --- Output: MEMORY.md files (top-down) ---
|
|
|
|
for dir in "${memory_dirs[@]}"; do
|
|
real_path="$(readlink -f "$dir/MEMORY.md")"
|
|
if [[ -z "${emitted_files[$real_path]:-}" ]]; then
|
|
emit_file "$dir/MEMORY.md"
|
|
emitted_files["$real_path"]=1
|
|
fi
|
|
done
|
|
|
|
# --- Output: BESTPRACTICES.md files (top-down) ---
|
|
|
|
for dir in "${bestpractices_dirs[@]}"; do
|
|
real_path="$(readlink -f "$dir/BESTPRACTICES.md")"
|
|
if [[ -z "${emitted_files[$real_path]:-}" ]]; then
|
|
emit_file "$dir/BESTPRACTICES.md"
|
|
emitted_files["$real_path"]=1
|
|
fi
|
|
done
|