From a463dc079387568a02113393c943025e8497175a Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Fri, 13 Mar 2026 11:49:40 +1300 Subject: [PATCH] Add context-load/start-claude scripts and clean up CLAUDE.md context-load walks from cwd upward, loading CLAUDE.md, CONTEXT.md, MEMORY.md, and BESTPRACTICES.md files with directory trees and git status. start-claude wraps the claude CLI with --append-system-prompt. CLAUDE.md: removed Ansible/Helm sections (in best-practices/), folded Validate Before Deploying into Process Principles, deduped secrets bullet, fixed best-practices path references. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 35 ++-------- scripts/context-load | 150 +++++++++++++++++++++++++++++++++++++++++++ scripts/start-claude | 25 ++++++++ 3 files changed, 179 insertions(+), 31 deletions(-) create mode 100755 scripts/context-load create mode 100755 scripts/start-claude diff --git a/CLAUDE.md b/CLAUDE.md index 694974b..2ad605d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,9 +15,9 @@ Scan the directories at runtime so the list is always current. Include a brief description if the project has a CLAUDE.md or README.md you can glean one from. 2. Based on the user's choice: - - **Existing project**: `cd` into the directory, read all `.md` files, and read `~/dev/claude/secrets/` (read-only reference — review every file to refresh context). Then read the [Best Practices Index](best-practices/INDEX.md) and load any topic files relevant to the selected project's technology stack. Ask clarifying questions if anything is unclear or incomplete, and note context in MEMORY.md. + - **Existing project**: `cd` into the directory, read all `.md` files, and read `~/dev/claude/secrets/` (read-only reference — review every file to refresh context). Then read the [Best Practices Index](best-practices/INDEX.md) in the `claude-foundations` repo and load any topic files relevant to the selected project's technology stack. Ask clarifying questions if anything is unclear or incomplete, and note context in MEMORY.md. - **No project right now**: Do nothing further — just respond normally. - - **New project!**: Follow the "New Projects" section below. Also read `~/dev/claude/secrets/` as above. Read the [Best Practices Index](best-practices/INDEX.md) and load topic files relevant to the new project's technology stack. + - **New project!**: Follow the "New Projects" section below. Also read `~/dev/claude/secrets/` as above. Read the [Best Practices Index](best-practices/INDEX.md) in the `claude-foundations` repo and load topic files relevant to the new project's technology stack. ## Secrets (`~/dev/claude/secrets/`) @@ -116,7 +116,7 @@ Three skills form a continuous learning pipeline across projects: 1. **`/log`** — Run at end of session. Captures decisions, gotchas, open questions to `memory/log/YYYY-MM-DD..md` in the current project. Also prunes old reflected logs. 2. **`/reflect-logs`** — Run periodically. Processes unprocessed session logs into topic memory files (`memory/gotchas-*.md`, `memory/process-lessons.md`, etc.). Flags stale entries. Tracks state in `.reflection-state.json`. -3. **`/distill-best-practices`** — Run from any project. Reads changed memory files across all tracked projects and proposes updates to `claude-foundations/best-practices/`. Tracks state in `best-practices/.distill-state.json`. +3. **`/distill-best-practices`** — Run from any project. Reads changed memory files across all tracked projects and proposes updates to the `claude-foundations` repo's `best-practices/` folder. Tracks state in `best-practices/.distill-state.json`. ### State Files - **`.reflection-state.json`** — Per-project, tracks which logs have been reflected on (md5 hashes of log file content) @@ -145,18 +145,6 @@ Break projects into numbered milestones (M1, M2, ...). Every milestone completio 3. **Updated README.md** — scripts section, milestone table, any new setup steps 4. **Updated CLAUDE.md** — repo structure, conventions, new patterns discovered -## Validate Before Deploying - -Every new config, manifest, or template should be validated locally before deploying. The target environment is not a test environment — each deploy-crash-fix cycle wastes time and clutters Git history. Batch fixes locally, push once. - -Examples: -- `helm template` for Helm values -- `kustomize build` for Kustomize apps -- `docker run validate-configuration` for app configs -- `docker inspect` for unfamiliar container images -- Lint/typecheck/test for application code -- `curl --resolve` for the full request chain after deployment - ## Version Management - Use the latest stable version of dependencies unless pinned for a reason @@ -170,7 +158,6 @@ Examples: - The `.sops.yaml` at the repo root defines path-based encryption rules - Filenames containing `secret` trigger SOPS encryption via pre-commit hooks - Non-secret files must NOT contain `secret` in their name -- Never pass secrets via command-line arguments (visible in `ps` output) — use `@file` references or environment variables - Keep unencrypted secrets in `local_secrets/` (gitignored) ## Scripting Conventions @@ -185,24 +172,10 @@ Examples: These are hard-won lessons from real project work: -- **Validate locally, deploy once.** Don't use the live environment as a test bed. Catch errors with local validation tools before pushing. +- **Validate locally, deploy once.** Don't use the live environment as a test bed. Catch errors with local validation tools before pushing (`helm template`, `kustomize build`, lint/typecheck, `docker inspect`, etc.). Batch fixes locally, push once. - **Check before you act.** Before writing firewall/network rules, check actual routing (`ip route get`). Before running config management with variables, ensure values are real, not placeholders. Before assuming a container has a shell, `docker inspect` it. - **Test the full chain immediately.** After wiring up a new service or endpoint, test end-to-end from the user's perspective right away. Don't assume intermediate steps working means the whole chain works. - **Verify scripts should be environment-resilient.** Avoid needing sudo or special access. Test from the accessible side of a connection. Use `curl --resolve` to bypass DNS/proxy layers when testing direct connectivity. - **Automate repeated sequences.** If you run the same 3+ commands in sequence more than once, it should become a script. - **Reflect after milestones.** Don't just finish — review what happened, what went wrong, what can be improved. Write it down so future sessions benefit. -## Ansible Conventions (where applicable) - -- Roles follow standard structure: `tasks/main.yml`, `templates/*.j2`, `handlers/main.yml` -- Jinja2 templates have `.j2` extension and include a "managed by Ansible" header comment -- Variables that need customisation go in `inventory.yml`, not scattered across role defaults -- Always pass `-i inventory.yml` explicitly or run from the directory containing `ansible.cfg` -- Never use placeholder values with `-e` for vars that template config files - -## Helm Chart Conventions (where applicable) - -- Always validate values against the chart schema before committing -- Run `helm show values / --version ` to check actual structure -- Schemas change between versions — field names and nesting can differ from docs or online examples -- A quick `helm template` test locally catches schema errors before deployment diff --git a/scripts/context-load b/scripts/context-load new file mode 100755 index 0000000..2f51f3f --- /dev/null +++ b/scripts/context-load @@ -0,0 +1,150 @@ +#!/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: +# - Every CLAUDE.md found walking up from cwd (top-down order) +# - Directory tree (depth 3) from each CLAUDE.md location +# - 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) +# - All *.md files in cwd +# - git-status-report from the highest-level CLAUDE.md dir +set -uo pipefail + +TREE_DEPTH=3 + +# --- Helpers --- + +emit_header() { + echo "" + echo "================================================================" + echo "=== $1" + echo "================================================================" + echo "" +} + +emit_file() { + local filepath="$1" + emit_header "FILE: $filepath" + cat "$filepath" + echo "" +} + +emit_tree() { + local dir="$1" + emit_header "TREE: $dir (depth $TREE_DEPTH)" + tree -L "$TREE_DEPTH" --charset utf-8 -I '.git|node_modules|__pycache__|.venv|venv' "$dir" 2>/dev/null \ + || find "$dir" -maxdepth "$TREE_DEPTH" -not -path '*/.git/*' -not -path '*/.git' | sort + echo "" +} + +strip_ansi() { + sed 's/\x1b\[[0-9;]*m//g' +} + +# --- 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=() +all_hierarchy_dirs=() + +while IFS= read -r dir; do + all_hierarchy_dirs+=("$dir") + [[ -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.md files (top-down) + tree from each --- + +# Track files we've already emitted to avoid duplicates +declare -A emitted_files + +for dir in "${claude_dirs[@]}"; do + # Resolve symlinks for dedup — two paths might point to the same file + real_path="$(readlink -f "$dir/CLAUDE.md")" + if [[ -z "${emitted_files[$real_path]:-}" ]]; then + emit_file "$dir/CLAUDE.md" + emitted_files["$real_path"]=1 + fi + emit_tree "$dir" +done + +# --- 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 + +# --- Output: all *.md files in cwd --- + +if compgen -G "$PWD"/*.md > /dev/null 2>&1; then + for md_file in "$PWD"/*.md; do + [[ -f "$md_file" ]] || continue + real_path="$(readlink -f "$md_file")" + if [[ -z "${emitted_files[$real_path]:-}" ]]; then + emit_file "$md_file" + emitted_files["$real_path"]=1 + fi + done +fi + +# --- Output: git status report --- + +if [[ ${#claude_dirs[@]} -gt 0 ]]; then + highest_dir="${claude_dirs[0]}" + git_status_report="$(command -v git-status-report 2>/dev/null || true)" + if [[ -n "$git_status_report" ]]; then + emit_header "GIT STATUS: $highest_dir" + "$git_status_report" "$highest_dir" 2>/dev/null | strip_ansi || true + fi +fi diff --git a/scripts/start-claude b/scripts/start-claude new file mode 100755 index 0000000..ce6a3d5 --- /dev/null +++ b/scripts/start-claude @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# start-claude — Launch Claude Code with pre-loaded project context +# +# Runs context-load to gather CLAUDE.md files, directory trees, +# CONTEXT.md files, cwd markdown, and git status, then passes +# the result as an appended system prompt. +# +# All arguments are forwarded to claude. +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CONTEXT_LOADER="${SCRIPT_DIR}/context-load" + +if [[ ! -x "$CONTEXT_LOADER" ]]; then + echo "Error: context-load not found at $CONTEXT_LOADER" >&2 + exit 1 +fi + +context="$("$CONTEXT_LOADER")" + +if [[ -n "$context" ]]; then + exec claude --append-system-prompt "$context" "$@" +else + exec claude "$@" +fi