diff --git a/ABOUT.md b/ABOUT.md new file mode 100644 index 0000000..a08768e --- /dev/null +++ b/ABOUT.md @@ -0,0 +1 @@ +description: Shared hooks, formatters, and knowledge pipeline for Claude Code sessions diff --git a/CLAUDE.md b/CLAUDE.md index 0344e5f..714a88d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,39 @@ # CLAUDE.md — General Project Guidelines +This file is the always-on guardrails. Topic-specific detail lives in [`claude/`](./claude/) (symlinked from `~/dev/claude/claude/`). Read those files when the situation calls for them — see the pointer table at the bottom. + +## Tone and Interaction + +**No hedge words.** Less is more. + +**No intro paragraph.** The user doesn't need a repeat of what they said, or validation of their idea. + +**No validation.** Give direct and honest feedback. Do not validate an idea just because it exists. + +**Question the question.** Before answering, check: is this the right question? Common failure modes: + +- **XY problem**: the question asks how to do X, but X is a means to an unstated goal Y with a better path. Name the Y and address that. +- **False premise**: the question assumes something that may be wrong. State the assumption and challenge it before answering. +- **Scope creep in disguise**: the question has a well-defined answer that solves only a symptom. Note the larger problem if it's obvious. + +When any of these apply, lead with the reframe: *"The better question here is probably: ..."* — then answer that. + +**One clarifying question, not many.** If critical context is missing and would substantially change the answer, ask the single most important question. Don't fire off a list. + ## Session Start -1. **Immediately** (without waiting for user input) use the directory tree already provided by `context-load` (the `TREE` section in your context) to identify project directories under `~/dev/claude/` (excluding `secrets/`). Present a numbered menu like: +**Before showing any picker, check for auto-selection signals:** + +- **From cwd:** if `$PWD` is `~/dev/claude/` or starts with `~/dev/claude//` (where `` is a known project directory, not `secrets/`), that project is auto-selected. +- **From active-mode.env:** if `$CLAUDE_CONFIG_DIR` is set and `$CLAUDE_CONFIG_DIR/active-mode.env` exists, read it. If `CLAUDE_PROJECT` is non-empty, that project is auto-selected. Also read `CLAUDE_MODE` and `CLAUDE_MODE_TAG` for the announcement below. +- **Cwd takes precedence** if both signals are present and disagree. + +If a project was auto-selected, **skip the picker** and open your first response with: +> Auto-selected project **<name>**. [Engagement mode: **<mode-tag>**] — starting load now. + +Then proceed directly to step 2. Otherwise: + +1. **Immediately** (without waiting for user input) use the project CLAUDE.md paths listed in the `CLAUDE.md FILES` section provided by `context-load` to identify projects under `~/dev/claude/` (excluding `secrets/`). Present a numbered menu like: > What are we working on today? > @@ -12,14 +43,14 @@ > N-1. **No project right now** — just chat > N. **New project!** — start something new - Do **not** run shell commands to list directories — the tree is already in your context. Include a brief description if the project has a CLAUDE.md or README.md you can glean one from. + Include a brief description from each project's ABOUT.md/CLAUDE.md/README.md. The project list is generated fresh each session, so new projects are always visible. -2. Based on the user's choice: - - **Existing project**: `cd` into the directory. **Pull the latest from the remote** (`git pull --ff-only`) before reading any files — work may have been pushed from another machine or agent. If the pull fails (diverged history, uncommitted changes), warn the user and ask how to proceed. Then read all `.md` files, and read `~/dev/claude/secrets/` (read-only reference — review every file to refresh context). Then read `BESTPRACTICES.md` (loaded automatically by `context-load`) 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 `BESTPRACTICES.md` (loaded automatically by `context-load`) and load topic files relevant to the new project's technology stack. +2. Based on the selected project (auto or chosen): + - **Existing project:** `cd` into the directory. **Pull the latest from the remote** (`git pull --ff-only`) before reading any files — work may have been pushed from another machine or agent. If the pull fails (diverged history, uncommitted changes), warn the user and ask how to proceed. Then read all `.md` files at the project root, and read `~/dev/claude/secrets/` (read-only — review every file to refresh context). Then read `BESTPRACTICES.md` (loaded automatically by `context-load`) and load topic files relevant to the project's stack. Ask clarifying questions if anything is unclear. + - **No project right now:** do nothing further — just respond normally. + - **New project!** Follow [`claude/new-projects.md`](./claude/new-projects.md). Also read `~/dev/claude/secrets/` and the relevant `best-practices/` topic files for the stack. -3. **Set the status line topic** after the user selects a project or describes their task. See the "Status Line" section below. +3. **Set the status line topic** once the user has selected a project or described their task. See [`claude/status-line.md`](./claude/status-line.md). ## Secrets (`~/dev/claude/secrets/`) @@ -28,298 +59,42 @@ - Files in `~/dev/claude/secrets/` are **read-only**. Never edit them. - **Never** copy, echo, write, or reproduce secret values into any other file — not MEMORY.md, not CLAUDE.md, not commit messages, not scripts, not tool output, nowhere. - **Never** include secret values in git commits, diffs, or changelogs of any project. -- **Never** pass secret values as command-line arguments (visible in `ps` output). Use `@file` references, environment variables sourced at runtime, or stdin. +- **Never** pass secret values as command-line arguments (visible in `ps`). Use `@file` references, env vars sourced at runtime, or stdin. - When a task requires a secret, read it at execution time from the secrets folder and use it ephemerally. Do not cache or persist the value. -- It is acceptable to reference the **existence** of a secret file (e.g., "credentials are in `~/dev/claude/secrets/gitea/ai_enablement`") but never its contents. - -## New Projects - -If this is a new project: - -1. Create a new directory under `~/dev/claude//` -2. **Ask the user** which Gitea user/org the repo should be created under (e.g., `homelab`, a personal user, etc.) before setting up the remote -3. Create the initial standard files: - - **CLAUDE.md** — Project-specific architecture, conventions, repo structure, and working instructions for Claude - - **MEMORY.md** — Persistent learnings, gotchas, reflections, and process improvements - - **FUTURE.md** — Ideas and improvements not on the active roadmap (Problem/Idea/Open questions/Depends on format) - - **README.md** — Human-readable overview, quick start, milestones, and scripts reference -4. Parse the other CLAUDE.md files from sibling project folders in `~/dev/claude/`, and based on the type of project being considered, bring over related practices, guidelines, and learnings -5. **For coding projects with multiple milestones:** Read `best-practices/spec-driven-development.md` and `best-practices/test-driven-development.md`. Create `SPEC.md` and `spec/` directory. Write specs before writing code. See the "Spec-Driven Development" section below. - -## Source Control - -- All projects are hosted on **Gitea** (`gitea.oreillyit.nz`) as the primary remote — prefer this hostname over `gitea.homelab.internal` (same instance, but the public name enables external access) -- Migrate existing remotes from `gitea.homelab.internal` to `gitea.oreillyit.nz` when convenient -- AI-focused projects go under the **`skynet`** org; infrastructure projects under **`homelab`** -- SSH workflows preferred. SSH config uses host aliases per Gitea user: - - `gitea.oreillyit.nz-homelab` → authenticates as `cluster-administrator` (key: `~/.ssh/gitea-cluster-admin`) - - `gitea.oreillyit.nz-ai-enablement` → authenticates as `ai_enablement` (key: `~/.ssh/gitea.ai-enablement`) -- Git remote URLs use the alias: `git@gitea.oreillyit.nz-:/.git` - - Example: `git@gitea.oreillyit.nz-ai-enablement:skynet/custom-claude-skills.git` -- Optionally push-mirror to GitHub for public visibility -- **Always pull before planning work** — run `git pull --ff-only` when entering a project. Work may have been pushed from another machine or by container agents. If the pull fails, warn the user before proceeding. -- Use meaningful commit messages; prefer small, focused commits over large batches -- Enable pre-commit hooks where appropriate (secret detection, linting, formatting) -- Never commit secrets in plaintext — use SOPS + age or equivalent encryption - -## Documentation Standards - -Every project maintains up to six standard markdown files. All root-level `.md` files are automatically loaded by `context-load` at session start, so they should be **thin indexes** pointing to detail files in subdirectories — not large documents themselves. - -### CLAUDE.md (per-project) -The primary reference for Claude sessions. Should contain: -- Project overview and architecture -- Repository structure (keep updated as the project evolves) -- Key design decisions with rationale -- Conventions and coding standards -- Environment details (IPs, URLs, credentials references) -- Common operations / how-to recipes - -### MEMORY.md (Tiered Memory System) -Long-running projects accumulate significant context. To keep MEMORY.md useful rather than bloated, use a **tiered memory** structure: - -**MEMORY.md** is a **thin index only** — one-line descriptions with links to topic files in `memory/`. No content lives in MEMORY.md itself. Think of it as a card catalog. - -**memory/** contains the actual content, split by topic: -- `memory/project-status.md` — Current milestone, what's next, blockers -- `memory/network.md` — IPs, VIPs, subnets, topology -- `memory/gotchas-.md` — Gotchas grouped by technology (e.g., `gotchas-cilium.md`, `gotchas-authelia.md`) -- `memory/process-lessons.md` — How-to-work-with-this-repo lessons for Claude -- `memory/m-reflection.md` — One file per milestone reflection (these are time-bound, so per-file is natural) -- `memory/decisions.md` — Architecture and design decisions made during planning - -**Principles:** -- **Split by topic, not by time.** A Cilium gotcha belongs in `gotchas-cilium.md` whether discovered in M5 or M8. -- **Milestone reflections are the exception** — inherently time-bound, one file per milestone. -- **Index descriptions matter.** They're used to decide what to read. "Cilium L2/LB gotchas and externalTrafficPolicy quirks" beats "cluster stuff". -- **Prune aggressively.** If a gotcha was fixed (e.g., chart version upgraded past the bug), delete it. Stale memory is worse than no memory. -- **Each memory file should be self-contained and greppable.** Include enough context that the file makes sense on its own. -- **Deduplicate with CLAUDE.md.** Conventions and patterns that are stable should live in CLAUDE.md. Memory files are for learnings, gotchas, and reflections that accumulate over time. If something in memory has graduated to a stable convention, move it to CLAUDE.md and remove it from memory. - -**When reading memory at session start:** Read MEMORY.md (the index), then selectively read topic files relevant to the current task. Don't read all memory files unless doing a broad review. - -**When writing memory after a milestone:** Create the reflection file, update any affected topic files (new gotchas, updated status), and update the index. - -### CONTEXT.md (Active Work Focus) -Tells Claude (and independent agents) what the project is currently working on. Follows the same index pattern as MEMORY.md. - -**CONTEXT.md** is a **thin index** — one-line descriptions with links to detail files in `context/`. It answers "what should I focus on right now?" - -**context/** contains the detail files, one per active work stream: -- `context/.md` — Background, status, what to test, future direction for that work stream - -**Principles:** -- **Keep it current.** Remove entries when work is complete. CONTEXT.md should reflect what's actively in progress, not a history of past work. -- **Link, don't inline.** The index stays small so `context-load` doesn't bloat the system prompt. Detail files can be as long as needed. -- **Orient agents.** CONTEXT.md is the primary mechanism for pointing independent agents (including those in Docker containers) at the right work. Each agent gets the full context load, with its starting prompt referencing the relevant CONTEXT.md entry. -- **Complement, don't duplicate.** CLAUDE.md has stable conventions. MEMORY.md has accumulated learnings. CONTEXT.md has the *current* focus and active tasks. - -### BESTPRACTICES.md (Best Practices Index) -Index of generalised best practices extracted from real project work via the `/distill-best-practices` skill. Only exists in `claude-foundations` — other projects inherit it via `context-load` walking up the directory hierarchy. - -**BESTPRACTICES.md** is a **thin index** — one-line descriptions with links to topic files in `best-practices/`. - -**best-practices/** contains self-contained topic files (e.g., `kubernetes.md`, `helm.md`, `secrets-management.md`). Read only the files relevant to the current project's technology stack. - -The `/distill-best-practices` skill maintains both the topic files and the index. - -### FUTURE.md -Backlog of improvement ideas, each with: -- **Problem:** What's painful or manual today -- **Idea:** What the improvement looks like -- **Open questions:** Unknowns to research before starting -- **Depends on:** Other items or milestones that should come first - -### README.md -Human-readable project documentation: -- Architecture summary -- Quick start / setup instructions -- Milestone table with status -- Scripts section listing every script with purpose and usage - -## Script & Skill Documentation - -Every script in `claude-foundations` and every skill in `custom-claude-skills` must have a corresponding memory file in `claude-foundations/memory/`: - -- **Scripts:** `memory/script-.md` — Purpose, usage, how it works, gotchas -- **Skills:** `memory/skill-.md` — Purpose, usage, how it works, gotchas, which projects use it - -Each memory file should be self-contained and referenced from `claude-foundations/MEMORY.md` (the index). This ensures that future sessions can quickly understand what tooling exists and how it works, without having to read every script and SKILL.md from scratch. - -**When creating a new script or skill:** Create the memory file and update the MEMORY.md index as part of the same commit. - -**When creating or editing a skill:** Run `validate-skill ` before committing. The validator catches known restriction violations that have repeatedly broken skills — `$VAR` in paths, `${VAR}` syntax, `$()` substitution, uncovered binaries in `allowed-tools`, and more. A skill must pass with zero errors before it is committed. Warnings should be reviewed but are acceptable. - -## Knowledge Distillation Pipeline - -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 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) -- **`best-practices/.distill-state.json`** — In claude-foundations, tracks git SHAs per project at time of last distillation -- **`settings.yaml`** — In claude-foundations, configures log retention (default 7 days), max logs per reflection run, and tracked project list - -### Log Format -Session logs use structured markdown with parseable section headers: Summary, Decisions, Gotchas Discovered (tagged with `[topic]` for routing), Open Questions, Key Context, Process Notes. Empty sections are omitted. - -### Pruning -- Reflected logs older than `log.retention_days` (default: 7) are automatically deleted by `/log` -- Unreflected logs older than `log.warn_unreflected_days` (default: 14) trigger a warning instead of deletion -- `/reflect-logs` flags stale memory entries (version-specific bugs that have been fixed, manual processes that have been automated) - -## Plan Mode - -When working in plan mode (`permission_mode: plan`): - -**Before calling `ExitPlanMode`**, always write the complete plan to a file in the project root: - -- **Filename:** `[MILESTONE]-[PURPOSE]-PLAN.md` — e.g. `M2-auth-PLAN.md`, `M3-monitoring-PLAN.md` - - MILESTONE: the milestone identifier (e.g. `M2`) or a short label if not milestone-scoped (e.g. `initial`) - - PURPOSE: a short kebab-case description of what the plan covers -- **Contents:** the full plan as developed in the planning conversation — steps, decisions, rationale, open questions -- **Location:** project root (same directory as CLAUDE.md) - -This file becomes the implementation reference for the session that follows plan mode. - -**Keep the plan updated during implementation.** After completing each phase or significant step, update the PLAN.md file: -- Mark the phase/step status as **Complete** -- Add key commits, references, or artifacts produced -- Document deviations from the original plan (what changed and why) -- Note verification results - -This ensures the plan stays accurate as a living document — useful for resuming across sessions, reflecting on the milestone, and understanding what actually happened vs. what was planned. - -## Spec-Driven Development - -**Any coding project with multiple milestones MUST have specs before code.** This is a hard requirement, not a suggestion. The workflow is: Plan → Spec → Test → Code. - -**At project start (or when starting a new milestone), always read:** -- `best-practices/spec-driven-development.md` — spec structure, requirement numbering, scenarios, maintenance -- `best-practices/test-driven-development.md` — edge case discovery, property-based testing, AI agent testing patterns - -### Required Artifacts - -Every multi-milestone coding project must have: - -1. **`SPEC.md`** — Index file at the project root. Lists all spec files with a "when to read" column. Same thin-index pattern as MEMORY.md. -2. **`spec/` directory** — One spec file per subsystem. Each spec follows the required structure: Overview, Responsibilities, Dependencies, Data Model, Requirements (numbered), Scenarios (given/when/then). -3. **Numbered requirements** — Each spec uses a prefix (e.g., `IG-1` for ingestion, `DB-1` for database). Requirements must be independently testable and unambiguous. -4. **Test files that reference spec IDs** — Every test function name includes its requirement ID: `test_ig3_dedup_by_message_ts`. - -### The Workflow - -1. **Plan** — Architecture decisions, milestone breakdown, technology choices (PLAN.md) -2. **Spec** — Detailed contracts, data models, interfaces, requirements, scenarios (spec/) -3. **Test** — Write tests from the spec before code exists. They should all fail. -4. **Code** — Implement until tests pass. Minimum code to satisfy the spec. -5. **Update** — If implementation reveals spec issues, update spec → test → code in that order. - -### When to Write Specs - -- **Before M1 implementation begins** — write specs for all subsystems in M1's scope -- **Before each subsequent milestone** — write specs for new subsystems, update existing specs for changes -- **Spec changes and test changes ship in the same commit** -- **Code changes that affect interfaces require spec changes in the same commit** - -### What Does NOT Need a Spec - -- Infrastructure-only projects (Helm values, Kustomize manifests, Ansible playbooks) — these are declarative, not behavioral -- Single-script utilities — a well-commented script with a test is sufficient -- Documentation-only changes - -## Milestones - -Break projects into numbered milestones (M1, M2, ...). Every milestone completion MUST include: - -1. **Verification script** (`scripts/verify-m.sh`) — automated checks confirming all milestone outcomes. Scripts should be idempotent, non-destructive, and return non-zero on failure. Use colour output (green/red) for pass/fail. -2. **Milestone reflection** in `memory/m-reflection.md` — review the entire conversation and capture: - - Process improvements (what slowed us down, wrong assumptions, backtracking) - - Key knowledge for reproduction (gotchas, version quirks, debugging detours) - - Scripts and automation opportunities (repeated command sequences → scripts) - - Future improvement ideas (add to FUTURE.md) - - Update affected topic files in `memory/` (new gotchas, updated status) and the MEMORY.md index -3. **Updated README.md** — scripts section, milestone table, any new setup steps -4. **Updated CLAUDE.md** — repo structure, conventions, new patterns discovered - -## Version Management - -- Use the latest stable version of dependencies unless pinned for a reason -- Verify versions from live sources (`helm search repo`, upstream docs, package registries) — don't rely on memory -- Document the reason in a comment if a version is intentionally pinned below latest -- Check compatibility matrices before upgrading (e.g., Talos ↔ Kubernetes, framework ↔ runtime) - -## Secrets Management - -- SOPS + age is the standard encryption tool across all projects -- 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 -- Keep unencrypted secrets in `local_secrets/` (gitignored) - -## Scripting Conventions - -- All scripts live in `scripts/` and run from the repository root -- Scripts should be idempotent and safe to re-run -- Use colour output for pass/fail indicators in verification scripts -- Verification scripts should check for default/insecure credentials and print remediation instructions on failure -- Scripts should exit non-zero on failure so `&&` chains work naturally -- **Never hardcode secrets, tokens, or access keys in scripts.** Accept them via environment variables, stdin, or `@file` references. If a script needs a secret at runtime, read it from `~/dev/claude/secrets/` or accept it as a parameter — never embed it. +- Referencing the **existence** of a secret file is fine (e.g., "credentials are in `~/dev/claude/secrets/gitea/ai_enablement`") — referencing the contents is not. ## Process Principles -These are hard-won lessons from real project work: +Hard-won lessons that apply across every project: +- **Read the gotcha before you diagnose.** When an issue surfaces (HTTP 4xx/5xx, hung connection, perpetual `OutOfSync`, "permission denied", silent drop, etc.), the FIRST step is to grep `~/dev/claude/projects/best-practices/.md` and the project's `memory/gotchas-*.md` for keywords matching the symptom AND the technologies involved. Search both the *symptom* (e.g., `permission denied`, `30s timeout`, `not matching`) and the *infrastructure* (e.g., `cilium`, `ipBlock`, `ESO`, `apiserver`). Do this BEFORE forming your first hypothesis. Turns a multi-hour investigation into a 5-minute lookup. If the gotcha isn't there, capture the new one when you solve it. - **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. +- **Reflect after milestones.** Don't just finish — review what happened, what went wrong, what can be improved. Write it down. -## Agent Runtimes Control Plane +## Version & Secrets Management (compact rules) -When submitting tasks to the agent-runtimes control plane (locally via Docker Compose or in K8s), always recommend the user run the **agent-monitor** in a separate terminal to track progress: +- Use the latest stable version of dependencies unless pinned for a reason. Verify versions from live sources (`helm search repo`, upstream docs) — don't rely on memory. Document the reason in a comment if pinned below latest. Check compatibility matrices before upgrading. +- SOPS + age is the standard encryption tool across all projects. `.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. Keep unencrypted secrets in `local_secrets/` (gitignored). -```bash -# In a second terminal — filter to your project and recent activity -~/dev/claude/projects/agent-runtimes/scripts/agent-monitor --filter "project=" --filter "age<20m" -``` +## Detail files — when to read -This gives a live view of task states, durations, and time since last status change. Adjust the `age` filter to suit the session — `20m` is a good default for active work, `1h` for longer-running tasks. - -For full control plane usage (submitting tasks, checking logs, cancelling, common workflows), see: -**`~/dev/claude/projects/agent-runtimes/readme/control-plane-operations.md`** - -## Status Line - -A persistent bar at the bottom of Claude Code shows the current topic, model, and context usage: `[Model Name] topic | N% context`. - -### Setting the Topic - -After the user selects a project or describes their task (i.e., after the first response where the status line has had a chance to run), set the topic: - -```bash -~/.claude/status/set-topic.sh "$(pwd)" "project-name: brief task description" -``` - -**Examples:** -- `~/.claude/status/set-topic.sh "$(pwd)" "brainiac-app: M2 web frontend"` -- `~/.claude/status/set-topic.sh "$(pwd)" "cluster-bootstrap: Cilium upgrade"` -- `~/.claude/status/set-topic.sh "$(pwd)" "General chat"` - -### When to Update - -- **Session start**: Set the topic once the user picks a project or task -- **Focus change**: Update if the user shifts to a different project or task mid-session -- **Keep it short**: Aim for `project: task` format, under ~40 characters - -### How It Works - -The status line script (`scripts/statusline.sh`) runs after each assistant message. It writes the session ID to `/tmp/claude-session-id-`, which `set-topic.sh` reads to find the correct per-session topic file at `~/.claude/status//claude-topic.txt`. - -**Early calls are safe:** If `set-topic.sh` is called before the status line has run (i.e., on the first message), the topic is queued to a pending file and automatically applied when the status line first runs after the next response. +Pull one of these in only when the situation calls for it. Listed in roughly descending frequency. +| File | Read when | +|---|---| +| [`claude/source-control.md`](./claude/source-control.md) | Touching git, creating repos, setting up remotes | +| [`claude/agent-runtimes-cp.md`](./claude/agent-runtimes-cp.md) | Dispatching work to agent containers / hosted CP | +| [`claude/status-line.md`](./claude/status-line.md) | Setting or updating the status line topic | +| [`claude/plan-mode.md`](./claude/plan-mode.md) | In plan mode — before calling `ExitPlanMode` | +| [`claude/spec-driven-development.md`](./claude/spec-driven-development.md) | Starting a milestone / writing or editing specs | +| [`claude/milestones.md`](./claude/milestones.md) | Completing a milestone (verification, reflection) | +| [`claude/documentation-standards.md`](./claude/documentation-standards.md) | Creating or restructuring project doc files (ABOUT/CLAUDE/MEMORY/CONTEXT/FUTURE/README) | +| [`claude/new-projects.md`](./claude/new-projects.md) | Creating a new project from scratch | +| [`claude/scripting-conventions.md`](./claude/scripting-conventions.md) | Writing a new script | +| [`claude/script-skill-documentation.md`](./claude/script-skill-documentation.md) | Authoring or editing a script in claude-foundations or a skill in custom-claude-skills | +| [`claude/knowledge-distillation.md`](./claude/knowledge-distillation.md) | Running `/log`, `/reflect-logs`, or `/distill-best-practices` | +| [`claude/ask-minimax-usage.md`](./claude/ask-minimax-usage.md) | Considering delegating a file-heavy task to MiniMax | diff --git a/FUTURE.md b/FUTURE.md new file mode 100644 index 0000000..fca031e --- /dev/null +++ b/FUTURE.md @@ -0,0 +1,48 @@ +# Future Ideas — claude-foundations + +Backlog of improvement ideas not on the active roadmap. + +--- + +## Standing Deployment Authorization in CLAUDE.md + +- **Problem:** Claude has all the tooling needed to run deployment steps (sops, ansible-playbook, kubectl, docker, git push) but must ask for confirmation every time because these are irreversible, shared-infrastructure operations. This makes multi-step milestone deployments slow and interactive. +- **Idea:** Add explicit standing authorization in CLAUDE.md for deployment operations — scoped to verified milestone changes (e.g., "Claude may run Ansible playbooks against VPS and bootstrap VM when deploying verified milestone changes"). Define the scope clearly so it doesn't become a blank cheque. +- **Open questions:** Should authorization be per-project or global? Should it require a passing verification script first? Should it be limited to specific Ansible tags or playbooks? +- **Depends on:** Pre-deployment checklist script (see below) + +--- + +## Pre-Deployment Checklist Script + +- **Problem:** Before deploying infrastructure changes, there's no automated way to validate that the repo is in a clean, deployable state. Claude needs a safety gate to run before taking irreversible deployment actions. +- **Idea:** A `scripts/pre-deploy-check.sh` in cluster-bootstrap that validates: clean git status, all secrets SOPS-encrypted (no plaintext in tracked files), kustomize builds cleanly for all overlays, Ansible syntax checks pass, no placeholder values in inventory. Returns non-zero on any failure. +- **Open questions:** Should it also check that the target services are healthy before deploying (kubectl health checks)? Should it produce a deployment plan summary (like terraform plan)? +- **Depends on:** Nothing — can be built anytime + +--- + +## Milestone Deployment Orchestration Script + +- **Problem:** Deploying a milestone involves 6-8 manual steps in a specific order (SOPS encrypt, git push, Ansible playbooks, kubectl operations, container builds). Each step is straightforward but the sequencing is error-prone and tedious. +- **Idea:** A `scripts/deploy-milestone.sh` in cluster-bootstrap that sequences all deployment steps: pre-deploy checks → SOPS encrypt → git commit + push → Ansible playbooks (with --diff) → wait for ArgoCD sync → S3/kubectl operations → container builds → post-deploy verification. Support `--dry-run` mode that prints what it would do without executing. +- **Open questions:** Should it be a single script or a Makefile with targets? Should it poll ArgoCD sync status or just wait a fixed time? How to handle partial failures (resume from last successful step)? +- **Depends on:** Pre-deployment checklist script, standing deployment authorization + +--- + +## Rollback Documentation Per Deployment Target + +- **Problem:** When Claude takes deployment actions, it needs to understand how to revert each one if something goes wrong. Currently rollback procedures are tribal knowledge. +- **Idea:** Document rollback procedures for each deployment target in cluster-bootstrap (e.g., "VPS: re-run Ansible with previous commit", "ArgoCD: kubectl rollout undo or git revert + push", "S3: delete bucket via garage CLI"). Could live in `docs/rollback.md` or per-service README files. +- **Open questions:** Should rollback be automated (rollback script) or just documented? Should the deploy script automatically create git tags as rollback points? +- **Depends on:** Nothing — can be written anytime + +--- + +## CacheAligner pattern for API-metered agent pipelines + +- **Problem:** When dispatching many sub-agent API calls (e.g. agent-runtimes), dynamic content in system prompts (dates, session IDs, UUIDs) defeats Anthropic's prompt cache. Cached prefixes get a 90% token cost discount, but any prefix change invalidates the cache. +- **Idea:** Extract volatile values (timestamps, session tokens, request IDs) from system prompts and relocate them to message endings. Keep the system prompt prefix stable across calls so the provider KV cache hits consistently. This is a design pattern, not a dependency — inspired by Headroom's CacheAligner (chopratejas/headroom). +- **Open questions:** How much of our agent prompt content is actually stable vs dynamic? What's the realistic cache hit rate improvement? Does Anthropic's cache window (5 min TTL) align with our dispatch cadence? +- **Depends on:** agent-runtimes reaching API-metered multi-agent workloads (M5+) diff --git a/MEMORY.md b/MEMORY.md index 12a3cb4..66cfa60 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -40,5 +40,7 @@ - [Decisions](memory/decisions.md) — Architecture and design decisions: pipeline design, linting system, context-load, CLAUDE.md structure - [Process Lessons](memory/process-lessons.md) — Working rules: git-status at session start, plan mode for architecture, hook exit codes, ssh-agent - [Gitea Gotchas](memory/gotchas-gitea.md) — Push permission denied when repo created by different API user than SSH alias -- [Skills Gotchas](memory/gotchas-skills.md) — Broken symlinks under set -e, mid-session skill discovery limitation +- [Skills Gotchas](memory/gotchas-skills.md) — Broken symlinks under set -e, mid-session skill discovery limitation, `$()` and `$VAR` bang-command rejection - [Statusline Gotchas](memory/gotchas-statusline.md) — set-topic.sh bootstrap race condition, md5sum -n flag consistency +- [Hooks Gotchas](memory/gotchas-hooks.md) — PreCompact `transcript_path` removed from input; derive from session_id+cwd +- [Python Gotchas](memory/gotchas-python.md) — `sys.exit()` swallowed by bare `except:` inside loops diff --git a/claude/agent-runtimes-cp.md b/claude/agent-runtimes-cp.md new file mode 100644 index 0000000..bbb637d --- /dev/null +++ b/claude/agent-runtimes-cp.md @@ -0,0 +1,64 @@ +# Agent Runtimes Control Plane + +## Which CP to use + +**Always use the hosted CP (`agents.oreillyit.nz`) for real agent work.** Localhost is for testing only. + +| Use case | CP to use | +|---|---| +| Real implementation tasks, spec work, any multi-step agent work | `https://agents.oreillyit.nz/api` | +| Smoke-testing a new task payload format, debugging CP behaviour locally | `http://localhost:8100` | + +Reason: workloads on the hosted CP are not coupled to this laptop's uptime. If the laptop sleeps or is closed, tasks on localhost stall or die. Tasks on the hosted CP keep running. + +Since the M11 dashboard deployment, Traefik routes `/` to the React dashboard and `/api/*` to the CP API. Always use `https://agents.oreillyit.nz/api` as the CP_URL — not the bare hostname. + +```bash +export CP_URL=https://agents.oreillyit.nz/api +scripts/dispatch-task --login ... # first time per session — opens browser for OIDC +scripts/agent-monitor --login --filter "project=" --filter "age<1h" +``` + +## Task dispatch — always use a template or workflow + +**Never submit tasks via raw `curl` without explicit user approval.** Raw curl bypasses: +- Harness selection (agents start in empty containers with no credentials or context) +- `pre_actions` clone (agents have no repo to work on) +- `agent_repo` persistence (work is lost when the container exits) +- `requires_tags` validation (tasks may be picked up by incompatible dispatchers) + +Default approach — always one of: +1. `scripts/dispatch-task --template ` for single implementation tasks +2. `scripts/dispatch-workflow` for multi-node DAG workflows +3. `/manual-workflow` skill for interactive workflows with human review gates + +## Template selection + +| Work type | Template | +|---|---| +| Backend/CP implementation (migrations, APIs) | `opus-code-repo` | +| Frontend implementation | `sonnet-code-repo` | +| Planning / architecture | `opus-planning` | +| Security review | `opus-security-review` | +| Spec writing | `opus-spec-writer` | +| Test writing | `opus-test-writer` | + +Standard params for all code templates: +```bash +--template-param repo_url=git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes.git \ +--template-param agent_repo_url=git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes-agents.git +``` + +If raw curl is genuinely needed (e.g., testing a new payload field), state the reason and get explicit user confirmation before submitting. + +## Monitoring + +Recommend the user run **agent-monitor** in a separate terminal: + +```bash +scripts/agent-monitor --login --filter "project=" --filter "age<1h" +``` + +Adjust `age` to suit the session — `1h` is a good default for hosted CP work since tasks persist across laptop sleep. + +For full control plane usage (submitting tasks, checking logs, cancelling, common workflows): **`~/dev/claude/projects/agent-runtimes/readme/control-plane-operations.md`**. diff --git a/claude/ask-minimax-usage.md b/claude/ask-minimax-usage.md new file mode 100644 index 0000000..32d067f --- /dev/null +++ b/claude/ask-minimax-usage.md @@ -0,0 +1,23 @@ +# Context Offloading with `/ask-minimax` + +`/ask-minimax` delegates a task to a MiniMax M2.7 sub-session that reads and writes files directly. Only a short summary flows back — file contents never load into this session. Reach for it when the **file payload dwarfs the answer payload**. + +## Use it for + +- Summarising large files (logs, dumps, generated reports >500 lines) +- Extracting specific facts from multiple files or long reference docs +- Generating big files (migrations, config bundles, fixtures, large docs) where the content does not need to flow back +- Format conversion of large files (CSV↔JSON, XML↔YAML, etc.) +- Bulk find-and-extract across many files where only the matches matter + +## Skip it for + +- Iterative design or debugging — the main session needs the content in context +- Small files (under a few hundred lines) — overhead exceeds savings +- Tasks where you will immediately re-read the result to act on it +- Anything requiring tools MiniMax cannot use (web fetch, MCP, browser, agent dispatch) +- Architecture or quality-sensitive output — MiniMax is for grunt work, not nuanced reasoning + +## How to delegate + +Pass file *paths*, not file *contents*. If you read the input files yourself before invoking, you have already paid the token cost the skill exists to avoid. diff --git a/claude/documentation-standards.md b/claude/documentation-standards.md new file mode 100644 index 0000000..c9aca12 --- /dev/null +++ b/claude/documentation-standards.md @@ -0,0 +1,75 @@ +# Documentation Standards + +Every project maintains standard markdown files. Index files (MEMORY.md, CONTEXT.md, BESTPRACTICES.md, SPEC.md, CLAUDE.md at root) are **thin indexes** pointing to detail files in subdirectories — not large documents themselves. + +## CLAUDE.md (per-project) + +The primary reference for Claude sessions. Should contain: +- Project overview and architecture +- Repository structure (keep updated as the project evolves) +- Key design decisions with rationale +- Conventions and coding standards +- Environment details (IPs, URLs, credentials references) +- Common operations / how-to recipes + +For long projects, follow the same thin-index pattern as MEMORY.md — keep CLAUDE.md as the always-on guardrails + a "when to read which detail file" pointer table, and put per-subsystem conventions in `claude/.md`. + +## MEMORY.md (Tiered Memory System) + +**MEMORY.md** is a **thin index only** — one-line descriptions with links to topic files in `memory/`. No content lives in MEMORY.md itself. + +**memory/** contains the actual content, split by topic: +- `memory/project-status.md` — current milestone, what's next, blockers +- `memory/network.md` — IPs, VIPs, subnets, topology +- `memory/gotchas-.md` — gotchas grouped by technology (`gotchas-cilium.md`, `gotchas-authelia.md`) +- `memory/process-lessons.md` — how-to-work-with-this-repo lessons for Claude +- `memory/m-reflection.md` — one file per milestone reflection (time-bound, per-file is natural) +- `memory/decisions.md` — architecture and design decisions made during planning + +Principles: +- **Split by topic, not by time.** A Cilium gotcha belongs in `gotchas-cilium.md` whether discovered in M5 or M8. +- **Milestone reflections are the exception** — inherently time-bound, one file per milestone. +- **Index descriptions matter.** "Cilium L2/LB gotchas and externalTrafficPolicy quirks" beats "cluster stuff". +- **Prune aggressively.** If a gotcha was fixed (e.g., chart upgraded past the bug), delete it. Stale memory is worse than no memory. +- **Each memory file should be self-contained and greppable.** +- **Deduplicate with CLAUDE.md.** Stable conventions live in CLAUDE.md. Memory holds learnings, gotchas, reflections. If something in memory has graduated to a stable convention, move it to CLAUDE.md and remove it from memory. + +When reading memory at session start: read MEMORY.md (the index), then selectively read topic files relevant to the current task. Don't read all memory files unless doing a broad review. + +When writing memory after a milestone: create the reflection file, update affected topic files, update the index. + +## CONTEXT.md (Active Work Focus) + +Thin index — one-line descriptions with links to detail files in `context/`. Answers "what should I focus on right now?" + +**context/** contains one file per active work stream: background, status, what to test, future direction. + +Principles: +- **Keep it current.** Remove entries when work is complete. CONTEXT.md reflects what's actively in progress, not history. +- **Link, don't inline.** Index stays small so `context-load` doesn't bloat the system prompt. +- **Orient agents.** CONTEXT.md is the primary mechanism for pointing independent agents (including container agents) at the right work. +- **Complement, don't duplicate.** CLAUDE.md = stable conventions. MEMORY.md = accumulated learnings. CONTEXT.md = *current* focus. + +## BESTPRACTICES.md (Best Practices Index) + +Index of generalised best practices extracted from real project work via `/distill-best-practices`. Only exists in `claude-foundations` — other projects inherit it via `context-load` walking up the directory hierarchy. + +Thin index pointing to topic files in `best-practices/` (e.g., `kubernetes.md`, `helm.md`). Read only files relevant to the current project's stack. + +`/distill-best-practices` maintains both the topic files and the index. + +## FUTURE.md + +Backlog of improvement ideas, each with: +- **Problem:** what's painful or manual today +- **Idea:** what the improvement looks like +- **Open questions:** unknowns to research before starting +- **Depends on:** other items or milestones that should come first + +## README.md + +Human-readable project documentation: +- Architecture summary +- Quick start / setup instructions +- Milestone table with status +- Scripts section listing every script with purpose and usage diff --git a/claude/knowledge-distillation.md b/claude/knowledge-distillation.md new file mode 100644 index 0000000..e8aba9d --- /dev/null +++ b/claude/knowledge-distillation.md @@ -0,0 +1,23 @@ +# Knowledge Distillation Pipeline + +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`. + +## State files + +- **`.reflection-state.json`** — per-project, tracks which logs have been reflected on (md5 hashes of log content) +- **`best-practices/.distill-state.json`** — in claude-foundations, tracks git SHAs per project at time of last distillation +- **`settings.yaml`** — in claude-foundations, configures log retention (default 7 days), max logs per reflection run, tracked project list + +## Log format + +Session logs use structured markdown with parseable section headers: Summary, Decisions, Gotchas Discovered (tagged with `[topic]` for routing), Open Questions, Key Context, Process Notes. Empty sections are omitted. + +## Pruning + +- Reflected logs older than `log.retention_days` (default: 7) are automatically deleted by `/log` +- Unreflected logs older than `log.warn_unreflected_days` (default: 14) trigger a warning instead of deletion +- `/reflect-logs` flags stale memory entries (version-specific bugs that have been fixed, manual processes that have been automated) diff --git a/claude/milestones.md b/claude/milestones.md new file mode 100644 index 0000000..fde5035 --- /dev/null +++ b/claude/milestones.md @@ -0,0 +1,17 @@ +# Milestones + +Break projects into numbered milestones (M1, M2, ...). Every milestone completion MUST include: + +1. **Verification script** (`scripts/verify-m.sh`) — automated checks confirming all milestone outcomes. Idempotent, non-destructive, returns non-zero on failure. Colour output (green/red) for pass/fail. + +2. **Milestone reflection** in `memory/m-reflection.md` — review the **entire conversation** from the start of the milestone (not just the final state) and capture: + - **Process improvements:** what slowed us down, wrong assumptions, backtracking, what could be automated. What would make this milestone faster if we redid it from scratch? + - **Key knowledge for reproduction:** critical facts, gotchas, non-obvious config details that someone (or a future Claude session) would need to recreate this milestone reliably. Include version-specific quirks, network/subnet constraints, debugging detours. + - **Scripts and automation:** existing scripts that proved valuable, new scripts that would condense multi-step manual processes, patterns that could be extracted. Look at the conversation history for command sequences run repeatedly — these are scripting candidates. + - **Future improvement ideas:** things that surfaced but don't belong in current scope. Add to `FUTURE.md` with the standard Problem/Idea/Open questions/Depends on format. + +3. **Updated README.md** — scripts section, milestone table, any new setup steps. + +4. **Updated CLAUDE.md** — repo structure, conventions, new patterns discovered. (After the split: this may mean updating a `claude/.md` detail file rather than the root CLAUDE.md.) + +5. **Update affected topic files in `memory/`** (new gotchas, updated status) and the MEMORY.md index. diff --git a/claude/new-projects.md b/claude/new-projects.md new file mode 100644 index 0000000..8423e2e --- /dev/null +++ b/claude/new-projects.md @@ -0,0 +1,22 @@ +# New Projects + +When the user picks "New project!" from the session-start menu, or otherwise asks to start a new project: + +1. Create a new directory under `~/dev/claude//` +2. **Ask the user** which Gitea user/org the repo should be created under (e.g., `homelab`, `skynet`, `oreillyit`, a personal user) before setting up the remote +3. Create the initial standard files: + - **ABOUT.md** — One-sentence project description (see `documentation-standards.md`). Picked up by `context-load` for the session-start menu. + - **CLAUDE.md** — Project-specific architecture, conventions, repo structure, and working instructions for Claude + - **MEMORY.md** — Thin index pointing to `memory/` topic files + - **FUTURE.md** — Backlog ideas (Problem/Idea/Open questions/Depends on format) + - **README.md** — Human-readable overview, quick start, milestones, scripts reference +4. Parse sibling project CLAUDE.md files in `~/dev/claude/` and bring over related practices, guidelines, and learnings that apply to the new project's stack +5. **For coding projects with multiple milestones:** Read `best-practices/spec-driven-development.md` and `best-practices/test-driven-development.md`. Create `SPEC.md` and `spec/` directory. Write specs before writing code — see `spec-driven-development.md`. + +## ABOUT.md format + +```markdown +description: One sentence describing what this project is +``` + +Every project **must** have an ABOUT.md. Keep the description under ~80 characters. diff --git a/claude/plan-mode.md b/claude/plan-mode.md new file mode 100644 index 0000000..18b2e84 --- /dev/null +++ b/claude/plan-mode.md @@ -0,0 +1,23 @@ +# Plan Mode + +When working in plan mode (`permission_mode: plan`): + +**Before calling `ExitPlanMode`**, always write the complete plan to a file in the project root: + +- **Filename:** `[MILESTONE]-[PURPOSE]-PLAN.md` — e.g. `M2-auth-PLAN.md`, `M3-monitoring-PLAN.md` + - MILESTONE: the milestone identifier (e.g. `M2`) or a short label if not milestone-scoped (e.g. `initial`) + - PURPOSE: a short kebab-case description of what the plan covers +- **Contents:** the full plan as developed in the planning conversation — steps, decisions, rationale, open questions +- **Location:** project root (same directory as CLAUDE.md) + +This file becomes the implementation reference for the session that follows plan mode. + +## Keep the plan updated during implementation + +After each phase or significant step, update PLAN.md: +- Mark the phase/step status as **Complete** +- Add key commits, references, or artifacts produced +- Document deviations from the original plan (what changed and why) +- Note verification results + +This keeps the plan accurate as a living document — useful for resuming across sessions, reflecting on the milestone, and understanding what actually happened vs. what was planned. diff --git a/claude/script-skill-documentation.md b/claude/script-skill-documentation.md new file mode 100644 index 0000000..7f02032 --- /dev/null +++ b/claude/script-skill-documentation.md @@ -0,0 +1,12 @@ +# Script & Skill Documentation + +Every script in `claude-foundations` and every skill in `custom-claude-skills` must have a corresponding memory file in `claude-foundations/memory/`: + +- **Scripts:** `memory/script-.md` — purpose, usage, how it works, gotchas +- **Skills:** `memory/skill-.md` — purpose, usage, how it works, gotchas, which projects use it + +Each memory file should be self-contained and referenced from `claude-foundations/MEMORY.md` (the index). Future sessions can then understand what tooling exists without reading every script and SKILL.md from scratch. + +**When creating a new script or skill:** create the memory file and update the MEMORY.md index as part of the same commit. + +**When creating or editing a skill:** run `validate-skill ` before committing. The validator catches known restriction violations that have repeatedly broken skills — `$VAR` in paths, `${VAR}` syntax, `$()` substitution, uncovered binaries in `allowed-tools`, and more. A skill must pass with zero errors before it is committed. Warnings should be reviewed but are acceptable. diff --git a/claude/scripting-conventions.md b/claude/scripting-conventions.md new file mode 100644 index 0000000..f3ee8be --- /dev/null +++ b/claude/scripting-conventions.md @@ -0,0 +1,8 @@ +# Scripting Conventions + +- All scripts live in `scripts/` and run from the repository root +- Scripts should be idempotent and safe to re-run +- Use colour output for pass/fail indicators in verification scripts +- Verification scripts should check for default/insecure credentials and print remediation instructions on failure +- Scripts should exit non-zero on failure so `&&` chains work naturally +- **Never hardcode secrets, tokens, or access keys in scripts.** Accept them via environment variables, stdin, or `@file` references. If a script needs a secret at runtime, read it from `~/dev/claude/secrets/` or accept it as a parameter — never embed it. diff --git a/claude/source-control.md b/claude/source-control.md new file mode 100644 index 0000000..f5eb26c --- /dev/null +++ b/claude/source-control.md @@ -0,0 +1,27 @@ +# Source Control + +## Hosts and orgs + +- All projects on **Gitea** (`gitea.oreillyit.nz`) as primary remote — prefer this hostname over `gitea.homelab.internal` (same instance, the public name enables external access) +- Migrate existing remotes from `gitea.homelab.internal` to `gitea.oreillyit.nz` when convenient +- **`skynet`** org: AI-focused projects. Owned by `ai_enablement`. +- **`homelab`** org: infrastructure projects (cluster-bootstrap, etc.). Owned by `cluster-administrator`. +- **`oreillyit`** org: non-AI internal O'Reilly IT tools. Owned by `ai_enablement`. +- Optionally push-mirror to GitHub for public visibility. + +## SSH aliases + +Pattern: `gitea.oreillyit.nz-`. + +- `gitea.oreillyit.nz-homelab` → authenticates as `cluster-administrator` (key: `~/.ssh/gitea-cluster-admin`) +- `gitea.oreillyit.nz-ai-enablement` → authenticates as `ai_enablement` (key: `~/.ssh/gitea.ai-enablement`) + +Git remote URL format: `git@gitea.oreillyit.nz-:/.git` +- Example: `git@gitea.oreillyit.nz-ai-enablement:skynet/custom-claude-skills.git` + +## Working rules + +- **Always pull before planning work** — run `git pull --ff-only` when entering a project. Work may have been pushed from another machine or by container agents. If the pull fails (diverged history, uncommitted changes), warn the user before proceeding. +- Meaningful commit messages; prefer small, focused commits over large batches. +- Enable pre-commit hooks where appropriate (secret detection, linting, formatting). +- Never commit secrets in plaintext — use SOPS + age or equivalent encryption. diff --git a/claude/spec-driven-development.md b/claude/spec-driven-development.md new file mode 100644 index 0000000..0b9fcc1 --- /dev/null +++ b/claude/spec-driven-development.md @@ -0,0 +1,37 @@ +# Spec-Driven Development + +**Any coding project with multiple milestones MUST have specs before code.** Hard requirement, not a suggestion. Workflow: Plan → Spec → Test → Code. + +At project start or when starting a new milestone, always read: +- `best-practices/spec-driven-development.md` — spec structure, requirement numbering, scenarios, maintenance +- `best-practices/test-driven-development.md` — edge case discovery, property-based testing, AI agent testing patterns + +## Required artifacts + +Every multi-milestone coding project must have: + +1. **`SPEC.md`** — Index file at the project root. Lists all spec files with a "when to read" column. Same thin-index pattern as MEMORY.md. +2. **`spec/` directory** — One spec file per subsystem. Each spec follows: Overview, Responsibilities, Dependencies, Data Model, Requirements (numbered), Scenarios (given/when/then). +3. **Numbered requirements** — Each spec uses a prefix (e.g., `IG-1` for ingestion, `DB-1` for database). Requirements must be independently testable and unambiguous. +4. **Test files that reference spec IDs** — every test function name includes its requirement ID: `test_ig3_dedup_by_message_ts`. + +## Workflow + +1. **Plan** — architecture decisions, milestone breakdown, technology choices (PLAN.md) +2. **Spec** — detailed contracts, data models, interfaces, requirements, scenarios (spec/) +3. **Test** — write tests from the spec before code exists. They should all fail. +4. **Code** — implement until tests pass. Minimum code to satisfy the spec. +5. **Update** — if implementation reveals spec issues, update spec → test → code in that order. + +## When to write specs + +- **Before M1 implementation begins** — write specs for all subsystems in M1's scope +- **Before each subsequent milestone** — specs for new subsystems, updates to existing specs for changes +- **Spec changes and test changes ship in the same commit** +- **Code changes that affect interfaces require spec changes in the same commit** + +## What does NOT need a spec + +- Infrastructure-only projects (Helm values, Kustomize manifests, Ansible playbooks) — declarative, not behavioural +- Single-script utilities — a well-commented script with a test is sufficient +- Documentation-only changes diff --git a/claude/status-line.md b/claude/status-line.md new file mode 100644 index 0000000..1a12716 --- /dev/null +++ b/claude/status-line.md @@ -0,0 +1,28 @@ +# Status Line + +A persistent bar at the bottom of Claude Code shows the current topic, model, and context usage: `[Model Name] topic | N% context`. + +## Setting the topic + +After the user selects a project or describes their task (i.e., after the first response where the status line has had a chance to run), set the topic: + +```bash +~/.claude/status/set-topic.sh "$(pwd)" "project-name: brief task description" +``` + +Examples: +- `~/.claude/status/set-topic.sh "$(pwd)" "brainiac-app: M2 web frontend"` +- `~/.claude/status/set-topic.sh "$(pwd)" "cluster-bootstrap: Cilium upgrade"` +- `~/.claude/status/set-topic.sh "$(pwd)" "General chat"` + +## When to update + +- **Session start:** set the topic once the user picks a project or task +- **Focus change:** update if the user shifts to a different project or task mid-session +- **Keep it short:** aim for `project: task` format, under ~40 characters + +## How it works + +The status line script (`scripts/statusline.sh`) runs after each assistant message. It writes the session ID to `/tmp/claude-session-id-`, which `set-topic.sh` reads to find the correct per-session topic file at `~/.claude/status//claude-topic.txt`. + +Early calls are safe: if `set-topic.sh` is called before the status line has run (first message), the topic is queued to a pending file and automatically applied when the status line first runs after the next response. diff --git a/memory/.reflection-state.json b/memory/.reflection-state.json new file mode 100644 index 0000000..b90ed4d --- /dev/null +++ b/memory/.reflection-state.json @@ -0,0 +1,7 @@ +{ + "version": 1, + "last_run": "2026-04-19T09:51:20Z", + "processed": { + "log/2026-04-13.140716.md": "a2e7bfbbe892117df48526fb236ea367" + } +} diff --git a/memory/decisions.md b/memory/decisions.md index ec5736c..f41e8a3 100644 --- a/memory/decisions.md +++ b/memory/decisions.md @@ -83,3 +83,23 @@ api-design.md and llm-code-security.md are separate files despite both being "se ## Review skills: Read-only output, not auto-edit /review-plan and /review-spec are read-only (allowed-tools: Read, Glob, Grep). Review output informs the human rather than auto-editing plans/specs. This avoids unintended changes and reduces permission prompts. Both skills load all 5 design/security best practice files upfront — better to over-load context than miss a relevant check. + +## Transcript backups: Sonnet (not Haiku) for /log transcript-analysis subagent + +Gotcha detection requires judgment about backtracking and failed attempts mid-session. Haiku tends to see only the final outcome and miss the failure mode that led to it. Sonnet in its own fresh context window produces usable transcript companion logs; Haiku did not. + +## Transcript backups: /log pre-gathers metadata only, subagent reads content + +`/log` calls `list-transcripts-here.sh` to pull compact JSON metadata (backup names, session IDs, paths) — no transcript content. Full JSONL content is read only by the Sonnet subagent in its own context window via `extract-transcripts.py --extract`. Keeps the parent skill's context small while still giving the subagent everything it needs. + +## Transcript backups: Tracking file is JSON at `~/.claude/transcript-backups/tracking.json` + +Machine-readable JSON (not a markdown table) because both the pre-compact hook (bash) and the /log skill / subagent (python) need to update it. JSON parses trivially from both; markdown tables do not. + +## Skill-helper scripts: Live in claude-foundations, symlinked to `~/.claude/scripts/` + +`~/.claude/` always resolves to the default profile directory regardless of `CLAUDE_CONFIG_DIR`, making `~/.claude/scripts/` a stable location accessible from all profiles. Scripts live canonically in `claude-foundations/scripts/` and `install-hooks.sh` symlinks them in. Same pattern as hooks. + +## install-hooks: Curated `SKILL_HELPERS` array, not blanket symlink + +Not every script in `claude-foundations/scripts/` belongs in `~/.claude/scripts/` — only those referenced by skills. `install-hooks.sh` keeps an explicit `SKILL_HELPERS` array as the authoritative list of which scripts to symlink. Regular scripts (statusline.sh, set-topic.sh, etc.) are accessed via their full repo path. diff --git a/memory/gotchas-hooks.md b/memory/gotchas-hooks.md new file mode 100644 index 0000000..1b6abeb --- /dev/null +++ b/memory/gotchas-hooks.md @@ -0,0 +1,13 @@ +# Hooks Gotchas + +## PreCompact hook no longer receives `transcript_path` in JSON input + +**Symptom:** `pre-compact-backup.sh` silently skipped every run — no backup file produced, no error. +**Cause:** Claude Code's PreCompact hook JSON input no longer includes `transcript_path` (changed from older versions). The hook was reading an empty value and short-circuiting. +**Fix:** Derive the transcript path from `session_id` and `cwd` instead: `~/.claude/projects/$(echo "$cwd" | tr '/' '-')/.jsonl`. Both fields are still present in the hook input JSON. + +## Hooks fail silently under `set -e` when an input field is missing + +**Symptom:** Hook appears to run (exit 0) but produces no output or side effects. +**Cause:** A missing jq-extracted field returns empty, then subsequent commands operate on the empty string and either no-op or skip conditional branches without error. +**Fix:** Always validate required inputs early in the hook: `if [ -z "$session_id" ] || [ -z "$cwd" ]; then echo "missing input" >&2; exit 1; fi`. Log failures to stderr so they surface in Claude Code hook diagnostics. diff --git a/memory/gotchas-python.md b/memory/gotchas-python.md new file mode 100644 index 0000000..56510b4 --- /dev/null +++ b/memory/gotchas-python.md @@ -0,0 +1,7 @@ +# Python Gotchas + +## `sys.exit()` inside bare `except: pass` is swallowed + +**Symptom:** A loop that should terminate on first match with `sys.exit(0)` kept iterating and printing multiple lines. +**Cause:** `sys.exit()` raises `SystemExit`, which is a `BaseException`. A bare `except:` (or `except Exception:`) inside the loop body catches it and continues iteration. +**Fix:** Use `break` instead of `sys.exit()` when the exit is inside an exception handler, or narrow the except clause to the specific exception you care about (e.g., `except (KeyError, ValueError):`). Never use bare `except:` — it masks `SystemExit`, `KeyboardInterrupt`, and real bugs. diff --git a/memory/gotchas-skills.md b/memory/gotchas-skills.md index 3b63371..ae94e66 100644 --- a/memory/gotchas-skills.md +++ b/memory/gotchas-skills.md @@ -16,7 +16,7 @@ **Symptom:** Bang-command like `git log --since="$(git log ...)"` fails even though `Bash(git *)` is in allowed-tools. **Cause:** The permission checker rejects any command containing `$()` subshells regardless of the outer pattern match. -**Fix:** Keep bang-commands simple. If complex logic is needed, have the skill instructions tell Claude to run it via tool calls instead. +**Fix:** Keep bang-commands simple. If complex logic is needed, either (a) have the skill instructions tell Claude to run it via tool calls instead, or (b) create a thin wrapper shell script that runs the `$()` substitution internally and call the wrapper from the bang-command (see `list-transcripts-here.sh` for the wrapper pattern). ## Relative paths in bang-commands resolve differently based on CWD diff --git a/memory/gotchas-statusline.md b/memory/gotchas-statusline.md new file mode 100644 index 0000000..b0c044a --- /dev/null +++ b/memory/gotchas-statusline.md @@ -0,0 +1,11 @@ +# Statusline Gotchas + +## set-topic.sh errors "No session ID found" on first message + +Symptom: `set-topic.sh` always fails on the first assistant message because `/tmp/claude-session-id-` doesn't exist yet — `statusline.sh` hasn't run to create it. +Fix: Implemented pending-topic queue. `set-topic.sh` writes to `/tmp/claude-pending-topic-` when session ID is missing. `statusline.sh` picks it up on next run. + +## md5sum hash inconsistency between scripts + +Symptom: Hash mismatch between `statusline.sh` and `set-topic.sh` when computing the cwd-based filename. +Fix: Both scripts must use `echo -n "$CWD" | md5sum` — the `-n` flag (no trailing newline) is critical for consistent hashes. diff --git a/memory/process-lessons.md b/memory/process-lessons.md index f6cc7fa..5e12bd9 100644 --- a/memory/process-lessons.md +++ b/memory/process-lessons.md @@ -51,3 +51,11 @@ The Edit tool fails if `old_string` matches multiple locations. When editing fil ## Best-practices repo is separate from claude-foundations Best practices live in skynet/best-practices (cloned at ~/dev/claude/projects/best-practices/). ~/dev/claude/BESTPRACTICES.md is a symlink to its index. context-load doesn't show the best-practices directory in the tree — remember it exists when working on best practices topics. + +## Keep skill and script symlinks in sync across all profiles + +Every profile (`~/.claude`, `~/.claude-octopus`, `~/.claude-oreillyit`) is independent — missing a symlink in one profile breaks that profile's slash commands without affecting others. When adding or renaming a skill or skill-helper script, verify symlinks exist in every active profile. Example that bit: `~/.claude-octopus/skills/switch-mode` symlink was missing while `~/.claude/skills/switch-mode` worked. Audit with `ls -la ~/.claude*/skills/` when touching skill layout. + +## Verify subagent output paths use the project's absolute path + +When `/log` (or any skill that spawns a subagent) writes files on the project's behalf, always verify the first real run that the subagent wrote to the project's **absolute** `memory/log/` path, not a relative path resolved from an unexpected cwd. A relative path silently lands in the wrong directory and the log looks missing. diff --git a/memory/script-context-load.md b/memory/script-context-load.md index 8c6ed90..caf72c9 100644 --- a/memory/script-context-load.md +++ b/memory/script-context-load.md @@ -9,22 +9,27 @@ Gathers project context for a Claude Code session by walking from cwd upward, co ## What it loads (in order) -1. Every `CLAUDE.md` found walking up from cwd (top-down order) + directory tree (depth 3) from each -2. Every `CONTEXT.md` found walking up (top-down) -3. Every `MEMORY.md` found walking up (top-down) -4. Every `BESTPRACTICES.md` found walking up (top-down) -5. All `*.md` files in cwd (deduped against already-emitted files) -6. `git-status-report` output from the highest-level CLAUDE.md directory +1. **Paths** to every `CLAUDE.md` found walking up from cwd (not contents — Claude Code loads those natively via `claudeMd`), annotated with `description:` from sibling `ABOUT.md` if present +2. **Paths** to every project `CLAUDE.md` found under each CLAUDE.md directory (depth 2-4) — for project discovery, also annotated with ABOUT.md descriptions +3. Every `CONTEXT.md` found walking up (top-down) — full contents +4. Every `MEMORY.md` found walking up (top-down) — full contents +5. Every `BESTPRACTICES.md` found walking up (top-down) — full contents + +## What it does NOT load + +- CLAUDE.md contents (loaded natively by Claude Code) +- Directory trees (Claude can run `tree` on demand for fresh results) +- README.md files (human-oriented, read on demand) +- git-status-report (moved to `/housekeeping` skill) ## How it works - Uses `discover_upward()` to walk from `$PWD` to `/`, collecting directories - Resolves symlinks via `readlink -f` to deduplicate (e.g., `~/dev/claude/CLAUDE.md` symlink to `claude-foundations/CLAUDE.md`) - Emits each file with a structured header (`=== FILE: ... ===`) for easy parsing -- Tree output uses `tree` with fallback to `find` -- ANSI codes are stripped from git-status-report output +- Uses `find` to discover project CLAUDE.md files under each hierarchy directory ## Gotchas -- Only walks **upward** from cwd — does not descend into subdirectories. Launch from within a project directory to get that project's context. +- Only walks **upward** from cwd for index files — but scans **downward** for project CLAUDE.md discovery. Launch from within a project directory to get that project's context. - Index files (MEMORY.md, CONTEXT.md, BESTPRACTICES.md) should be thin indexes, not large documents, since they're injected into the system prompt. diff --git a/memory/skill-context-load.md b/memory/skill-context-load.md index 5155d5e..0040167 100644 --- a/memory/skill-context-load.md +++ b/memory/skill-context-load.md @@ -17,12 +17,12 @@ Run from any project directory. The skill will gather context from cwd upward, j ## How it works -- Uses `!`context-load`` to run the `scripts/context-load` script at skill load time -- The script output (CLAUDE.md files, trees, CONTEXT.md, MEMORY.md, BESTPRACTICES.md, git status) is injected directly into the skill prompt +- Uses `` !`context-load` `` to run the `scripts/context-load` script at skill load time +- The script output (CLAUDE.md paths, project listing, CONTEXT.md, MEMORY.md, BESTPRACTICES.md) is injected directly into the skill prompt - Claude reads and internalizes the output, then confirms what it loaded ## Gotchas - Depends on `context-load` being on `$PATH` (symlinked to `~/sbin/context-load`) -- Output size scales with the number of projects in the directory hierarchy — deep nesting or large index files may use significant tokens +- Output is lightweight (~50-200 lines) — CLAUDE.md contents are not included since Claude Code loads them natively - Only loads index files, not topic files from `memory/` or `context/` — Claude must use Read tool for those if needed diff --git a/memory/skill-end-session.md b/memory/skill-end-session.md new file mode 100644 index 0000000..8a950f2 --- /dev/null +++ b/memory/skill-end-session.md @@ -0,0 +1,33 @@ +# skill: /end-session + +**Location:** `custom-claude-skills/skills/end-session/SKILL.md` + +## Purpose + +End-of-session wrap-up that combines session logging, CONTEXT.md update, and project doc refresh into a single command. Replaces the manual workflow of running `/log` then separately updating CONTEXT.md and project docs. + +## Usage + +``` +/end-session +``` + +No arguments. Reviews the full conversation history automatically. + +## How it works + +1. **Session log** (Phase 1): Creates `memory/log/YYYY-MM-DD..md` with structured entries (Summary, Decisions, Gotchas, Open Questions, Key Context, Process Notes). Prunes old reflected logs per retention settings. +2. **Context update** (Phase 2): Assesses what's in progress, updates CONTEXT.md index and `context/.md` detail files so the next session can resume without conversation history. +3. **Doc refresh** (Phase 3): Updates FUTURE.md (new ideas), README.md (milestone table, scripts section), and CLAUDE.md (repo structure, conventions) -- only where this session actually changed something. +4. **Summary** (Phase 4): Prints what was logged, what the next session should pick up, and which docs were updated. + +## Relationship to other skills + +- Subsumes `/log` -- no need to run `/log` separately when using `/end-session` +- Does NOT include milestone reflection -- use `/reflect M` separately when a milestone is complete +- CONTEXT.md updates follow the same thin-index pattern documented in CLAUDE.md + +## Gotchas + +- Pre-gathers git log, git status, MEMORY.md, CONTEXT.md, FUTURE.md at skill load time -- if you made changes after invoking the skill, the pre-gathered data may be stale (but the skill can re-read files via tool calls) +- `[topic]` tags on gotchas should match existing memory file topics for routing by `/reflect-logs` diff --git a/scripts/context-load b/scripts/context-load index 9428aef..f6d5682 100755 --- a/scripts/context-load +++ b/scripts/context-load @@ -5,17 +5,13 @@ # 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 +# - 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) -# - All *.md files in cwd -# - git-status-report from the highest-level CLAUDE.md dir set -uo pipefail -TREE_DEPTH=3 - # --- Helpers --- emit_header() { @@ -33,16 +29,15 @@ emit_file() { 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|customers' "$dir" 2>/dev/null \ - || find "$dir" -maxdepth "$TREE_DEPTH" -not -path '*/.git/*' -not -path '*/.git' -not -path '*/customers/*' -not -path '*/customers' | sort - echo "" -} - -strip_ansi() { - sed 's/\x1b\[[0-9;]*m//g' +# 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 --- @@ -65,10 +60,8 @@ 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") @@ -90,21 +83,53 @@ else emit_header "CLAUDE PROFILE: $profile_name ($profile_dir)" fi -# --- Output: CLAUDE.md files (top-down) + tree from each --- +# --- 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 - # 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" + 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 - emit_tree "$dir" 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 @@ -134,27 +159,3 @@ for dir in "${bestpractices_dirs[@]}"; do 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/statusline.sh b/scripts/statusline.sh index 0c91717..eb65065 100755 --- a/scripts/statusline.sh +++ b/scripts/statusline.sh @@ -9,6 +9,26 @@ DATA=$(cat) SESSION_ID=$(echo "$DATA" | jq -r '.session_id // empty') MODEL=$(echo "$DATA" | jq -r '.model.display_name // "unknown"') CONTEXT_PCT=$(echo "$DATA" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1) +FIVE_HR=$(echo "$DATA" | jq -r '.rate_limits.five_hour.used_percentage // empty' | cut -d. -f1) +SEVEN_DAY=$(echo "$DATA" | jq -r '.rate_limits.seven_day.used_percentage // empty' | cut -d. -f1) + +MODE_TAG="" +MODEL_DISPLAY="$MODEL" +if [[ -n "${CLAUDE_CONFIG_DIR:-}" && -f "$CLAUDE_CONFIG_DIR/active-mode.env" ]]; then + env_file="$CLAUDE_CONFIG_DIR/active-mode.env" + MODE_TAG=$(grep '^CLAUDE_MODE_TAG=' "$env_file" 2>/dev/null | cut -d= -f2- || true) + raw_driver=$(grep '^CLAUDE_DRIVER=' "$env_file" 2>/dev/null | cut -d= -f2- || true) + escalates_to=$(grep '^CLAUDE_ESCALATES_TO=' "$env_file" 2>/dev/null | cut -d= -f2- || true) + if [[ -n "$raw_driver" ]]; then + # Capitalize first letter of driver (sonnet→Sonnet, haiku→Haiku, opus→Opus) + driver_cap="$(tr '[:lower:]' '[:upper:]' <<< "${raw_driver:0:1}")${raw_driver:1}" + if [[ "$escalates_to" == "opus" ]]; then + MODEL_DISPLAY="${driver_cap}→Opus" + else + MODEL_DISPLAY="$driver_cap" + fi + fi +fi TOPIC="" if [[ -n "$SESSION_ID" ]]; then @@ -33,8 +53,23 @@ if [[ -n "$SESSION_ID" ]]; then fi fi -if [[ -n "$TOPIC" ]]; then - echo "[$MODEL] $TOPIC | ${CONTEXT_PCT}% context" -else - echo "[$MODEL] ${CONTEXT_PCT}% context" +USAGE="" +if [[ -n "$FIVE_HR" ]]; then + USAGE=" | 5h: ${FIVE_HR}%" + if [[ -n "$SEVEN_DAY" ]]; then + USAGE="$USAGE 7d: ${SEVEN_DAY}%" + fi +fi + +# Prepend mode tag to topic if set +if [[ -n "$MODE_TAG" && -n "$TOPIC" ]]; then + TOPIC="${MODE_TAG} · ${TOPIC}" +elif [[ -n "$MODE_TAG" ]]; then + TOPIC="$MODE_TAG" +fi + +if [[ -n "$TOPIC" ]]; then + echo "[$MODEL_DISPLAY] $TOPIC | ${CONTEXT_PCT}% context${USAGE}" +else + echo "[$MODEL_DISPLAY] ${CONTEXT_PCT}% context${USAGE}" fi diff --git a/settings.yaml b/settings.yaml index aa645ba..5f1d7b4 100644 --- a/settings.yaml +++ b/settings.yaml @@ -11,10 +11,22 @@ distill: projects_dir: projects # Relative to CLAUDE_PROJECT_ROOT projects: # Projects to scan for memory changes - agent-runtimes + - ai-image-gen + - brainiac-app - claude-foundations - cluster-apps/octopus-deploy - cluster-bootstrap + - contracts + - crud-accelerator - custom-claude-skills + - dns-manager - hugo-accelerator + - small-scripts extra_projects: # Projects outside projects_dir (paths relative to CLAUDE_PROJECT_ROOT) - - path: small-scripts + - path: octopus/ai-assisted-migration + - path: octopus/customer-issue-sync + - path: octopus/customers/duel-image + - path: octopus/customers/slb + - path: octopus/goes + - path: octopus/PlatformHub-Demo + - path: octopus/the-case-for-agentic