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 <noreply@anthropic.com>
This commit is contained in:
35
CLAUDE.md
35
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.
|
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:
|
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.
|
- **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/`)
|
## 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.<HHMMSS>.md` in the current project. Also prunes old reflected logs.
|
1. **`/log`** — Run at end of session. Captures decisions, gotchas, open questions to `memory/log/YYYY-MM-DD.<HHMMSS>.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`.
|
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
|
### State Files
|
||||||
- **`.reflection-state.json`** — Per-project, tracks which logs have been reflected on (md5 hashes of log file content)
|
- **`.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
|
3. **Updated README.md** — scripts section, milestone table, any new setup steps
|
||||||
4. **Updated CLAUDE.md** — repo structure, conventions, new patterns discovered
|
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 <app> 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
|
## Version Management
|
||||||
|
|
||||||
- Use the latest stable version of dependencies unless pinned for a reason
|
- 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
|
- The `.sops.yaml` at the repo root defines path-based encryption rules
|
||||||
- Filenames containing `secret` trigger SOPS encryption via pre-commit hooks
|
- Filenames containing `secret` trigger SOPS encryption via pre-commit hooks
|
||||||
- Non-secret files must NOT contain `secret` in their name
|
- 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)
|
- Keep unencrypted secrets in `local_secrets/` (gitignored)
|
||||||
|
|
||||||
## Scripting Conventions
|
## Scripting Conventions
|
||||||
@@ -185,24 +172,10 @@ Examples:
|
|||||||
|
|
||||||
These are hard-won lessons from real project work:
|
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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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 <repo>/<chart> --version <ver>` 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
|
|
||||||
|
|||||||
150
scripts/context-load
Executable file
150
scripts/context-load
Executable file
@@ -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
|
||||||
25
scripts/start-claude
Executable file
25
scripts/start-claude
Executable file
@@ -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
|
||||||
Reference in New Issue
Block a user