Claude Code's Bash permission checker rejects \${VAR} parameter
substitution in skill ! commands. Replace all occurrences with \$VAR
(identical shell expansion) across log, reflect-logs, and
distill-best-practices skills. Rewrite \${VAR:-default} to use
test -n / || pattern instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
166 lines
6.8 KiB
Markdown
166 lines
6.8 KiB
Markdown
---
|
|
name: distill-best-practices
|
|
description: >
|
|
Cross-project best practices distillation. Reads changed memory files from all tracked
|
|
projects and proposes additions, updates, or removals to claude-foundations/best-practices/.
|
|
Run from any project — always targets claude-foundations as output. Interactive — presents
|
|
proposals for approval before making changes.
|
|
allowed-tools: Read, Edit, Write, Glob, Grep, Bash(git -C *), Bash(git rev-parse *), Bash(ls *), Bash(cat *), Bash(head *), Bash(date *)
|
|
---
|
|
|
|
# Distill Best Practices Skill
|
|
|
|
You are extracting generalisable best practices from project-specific memory files across all tracked projects.
|
|
|
|
## Pre-gathered context
|
|
|
|
### CLAUDE_PROJECT_ROOT
|
|
!`test -n "$CLAUDE_PROJECT_ROOT" && echo "$CLAUDE_PROJECT_ROOT" || echo "NOT_SET"`
|
|
|
|
### Distill state
|
|
!`cat $CLAUDE_PROJECT_ROOT/projects/claude-foundations/best-practices/.distill-state.json 2>/dev/null || echo "{}"`
|
|
|
|
### Settings
|
|
!`cat $CLAUDE_PROJECT_ROOT/projects/claude-foundations/settings.yaml 2>/dev/null || echo "Settings not found — CLAUDE_PROJECT_ROOT may not be set"`
|
|
|
|
### Current best-practices index
|
|
!`cat $CLAUDE_PROJECT_ROOT/projects/claude-foundations/BESTPRACTICES.md 2>/dev/null || echo "Index not found"`
|
|
|
|
### Available best-practices files
|
|
!`ls -1 $CLAUDE_PROJECT_ROOT/projects/claude-foundations/best-practices/ 2>/dev/null || echo "No best-practices directory"`
|
|
|
|
### Projects directory listing
|
|
!`ls -1d $CLAUDE_PROJECT_ROOT/projects/*/ 2>/dev/null || echo "No projects directory"`
|
|
|
|
## Instructions
|
|
|
|
### Step 0: Detect project root
|
|
|
|
Check the pre-gathered `CLAUDE_PROJECT_ROOT` value above. If it shows `NOT_SET` or the pre-gathered context is missing:
|
|
|
|
1. Walk up from the current working directory to find the **highest** parent directory containing a `CLAUDE.md` file — that is the project root.
|
|
2. Use that path as `PROJECT_ROOT` for all subsequent steps.
|
|
|
|
If `CLAUDE_PROJECT_ROOT` was set, use that value as `PROJECT_ROOT`.
|
|
|
|
The key paths derived from the root:
|
|
- **Settings**: `<PROJECT_ROOT>/projects/claude-foundations/settings.yaml`
|
|
- **Best practices dir**: `<PROJECT_ROOT>/projects/claude-foundations/best-practices/`
|
|
- **Best practices index**: `<PROJECT_ROOT>/projects/claude-foundations/BESTPRACTICES.md`
|
|
- **Distill state**: `<PROJECT_ROOT>/projects/claude-foundations/best-practices/.distill-state.json`
|
|
|
|
### Step 1: Discover changes per project
|
|
|
|
Read `settings.yaml` (pre-gathered above, or read it now if Step 0 detected the root manually).
|
|
|
|
For each project in `distill.projects`, get its path: `<PROJECT_ROOT>/<projects_dir>/<project_name>`.
|
|
Also process any `distill.extra_projects` entries — these have a `path` field relative to `PROJECT_ROOT`.
|
|
|
|
For each project (from both lists):
|
|
|
|
1. Get current HEAD: `git -C <path> rev-parse HEAD`
|
|
2. Look up `last_sha` from `.distill-state.json` for this project
|
|
3. If SHA matches → skip this project (no changes)
|
|
4. If `last_sha` exists → find changed files: `git -C <path> diff --name-only <last_sha>..HEAD -- memory/`
|
|
5. If `last_sha` is missing (first run) → list all memory files: `ls <path>/memory/*.md`
|
|
|
|
Filter to only `memory/*.md` files (exclude `memory/log/` — those are raw, unprocessed).
|
|
|
|
If no projects have changes, say so and stop.
|
|
|
|
### Step 2: Read changed memory files
|
|
|
|
For each changed memory file, read its full content using the Read tool.
|
|
|
|
Also identify which existing best-practices topic files cover the same domain. Use this mapping as a guide:
|
|
- `gotchas-k8s.md` → `kubernetes.md`
|
|
- `gotchas-cilium.md` → `kubernetes.md`
|
|
- `gotchas-helm.md` → `helm.md`
|
|
- `gotchas-ansible.md` → `ansible.md`
|
|
- `gotchas-sops.md` → `secrets-management.md`
|
|
- `process-lessons.md` → `validation.md`, `debugging.md`
|
|
- `decisions.md` → various (match by content)
|
|
- Other gotchas → match by topic or propose a new file
|
|
|
|
Read the matching best-practices files so you can compare.
|
|
|
|
### Step 3: Analyse and propose
|
|
|
|
For each potential change, classify it:
|
|
|
|
- **ADD**: A lesson that is project-agnostic and valuable across projects. When generalising:
|
|
- Strip project-specific details (IPs, namespace names, service names, hostnames)
|
|
- Replace specifics with generic descriptions (e.g., "10.111.0.5" → "the DNS server IP")
|
|
- Keep the principle and the reasoning — lose the implementation detail
|
|
- Only promote lessons that would apply to at least one other project type
|
|
|
|
- **UPDATE**: An existing best-practice entry that has new supporting evidence, needs refinement, or should be expanded with a new example.
|
|
|
|
- **REMOVE**: A best-practice that has been invalidated — version-specific bug fixed, tool changed, approach superseded. Cross-reference: if the source gotcha was removed from the project's memory, the best-practice may be stale.
|
|
|
|
### Step 4: Present proposals
|
|
|
|
**Do NOT make any changes yet.** Present a numbered list of proposals:
|
|
|
|
```
|
|
Proposals:
|
|
|
|
1. ADD to kubernetes.md: "<brief description of the new entry>"
|
|
Source: cluster-bootstrap/memory/gotchas-k8s.md
|
|
|
|
2. UPDATE validation.md: "<what changes and why>"
|
|
Source: cluster-bootstrap/memory/process-lessons.md
|
|
|
|
3. REMOVE helm.md: "<entry to remove and why it's stale>"
|
|
Reason: Source gotcha removed in cluster-bootstrap commit abc1234
|
|
```
|
|
|
|
Wait for the user to approve, modify, or reject proposals. The user may say "all", give specific numbers, or ask for changes.
|
|
|
|
### Step 5: Apply approved changes
|
|
|
|
For each approved proposal:
|
|
- **ADD**: Append the new entry to the target file, matching the existing style (heading level, bullet format, explanation depth)
|
|
- **UPDATE**: Edit the existing entry in place
|
|
- **REMOVE**: Delete the entry from the file
|
|
|
|
If a new best-practices topic file is needed:
|
|
- Create it following the format of existing files (top-level heading, subheadings per entry, 2-6 lines per entry)
|
|
- Add it to `<PROJECT_ROOT>/projects/claude-foundations/BESTPRACTICES.md` with a one-line description
|
|
|
|
### Step 6: Update distill state
|
|
|
|
Write `<PROJECT_ROOT>/projects/claude-foundations/best-practices/.distill-state.json`:
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"last_run": "<ISO-8601 timestamp>",
|
|
"projects": {
|
|
"<project_name>": {
|
|
"path": "<absolute_path>",
|
|
"last_sha": "<current HEAD sha>",
|
|
"last_run": "<ISO-8601 timestamp>"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Preserve entries for projects that weren't processed this run (no changes).
|
|
|
|
### Step 7: Summary
|
|
|
|
Print:
|
|
- Projects scanned and number of changed memory files per project
|
|
- Number of proposals (add/update/remove)
|
|
- Number approved and applied
|
|
- Any new best-practices files created
|
|
|
|
## Quality checks
|
|
|
|
- Best practices must be **project-agnostic** — no hardcoded IPs, namespaces, or service names
|
|
- Each entry should include the **principle and reasoning**, not just the rule
|
|
- Entries must be **deduplicated** against existing best-practices content
|
|
- The **BESTPRACTICES.md** must stay accurate after any file additions
|
|
- Proposals are **always presented before applying** — never auto-apply
|