Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .chezmoiignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ install.sh
{{ if or (not .alt_tools) (has "bat" .machine_excludes) }}.config/bat{{ end }}
{{ if or (not .alt_tools) (has "eza" .machine_excludes) }}.config/eza{{ end }}
{{ if not .macos_utils }}.hammerspoon{{ end }}
{{ if has "keepassxc" .machine_excludes }}.local/bin/kp-env{{ end }}
{{ if not .touch_id_sudo }}.chezmoiscripts/enable-touchid-sudo.sh{{ end }}

# sheldon lock file and static cache are generated at runtime
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ jobs:
persist-credentials: false
- uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
with:
globs: "**/*.md"
globs: |
**/*.md
!dot_claude/**

zizmor:
uses: drumandbytes/reusable-actions/.github/workflows/zizmor.yml@v1
Expand Down
3 changes: 3 additions & 0 deletions dot_Brewfile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ brew "caddy"
{{ if not (has "siderolabs/tap/talosctl" .machine_excludes) -}}
brew "siderolabs/tap/talosctl"
{{ end -}}
{{ if not (has "google-chrome" .machine_excludes) -}}
cask "google-chrome"
{{ end -}}
# pkg:end:dev_apps
{{ end -}}

Expand Down
28 changes: 28 additions & 0 deletions dot_claude/CLAUDE.md.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Git commits
Never add a "Co-Authored-By: Claude ..." trailer (or any self-attribution) to git commit messages. This applies in every project, overriding any default commit-workflow instructions that say otherwise.

# Response style

Be terse. Cut filler, hedging, and pleasantries ("Sure!", "I'd be happy to", "Great question") — start directly with the substance.

- No preamble before tool calls ("Let me check that", "I'll now look at..."). Just call the tool.
- No restating the question or narrating what you're about to do — show the result.
- No trailing recap of what you just did unless the user asked for a summary or it materially helps them (e.g. multi-step changes across files).
- Skip decorative headers, bullet-for-everything formatting, or emoji unless the content genuinely needs structure to stay readable.
- Prefer short direct sentences over long ones, but don't fragment to the point of dropping words that carry meaning (not/never/no/only), units, or exact numbers.
- Full prose is fine and expected for genuine explanations, prose is not the enemy — the target is zero throat-clearing, not artificial brevity that costs clarity.
- Never drop this for code, comments, commit messages, or docs — those get normal, complete prose.

# Code comments

Never restate what a signature/type already says (no `@param`/`@returns` duplicating an explicit type). Cut file-header banners and passive corporate phrasing ("this module is responsible for..."). Keep terse dev shorthand and honest tags (`TODO`/`FIXME`/`HACK`). Cleaning up comments in an existing file → invoke `humanize-comments` instead of reinventing the pass ad hoc.

# Compact instructions

When compacting, preserve: code changes made so far (files touched, what changed), open TODOs / next steps, test or build output (failures verbatim, not paraphrased), and decisions with their rationale (why an approach was chosen over alternatives). Drop exploratory chatter, superseded plans, and dead-end investigation paths.

@SHELL.md
{{ if not (has "drumandbytes" .machine_excludes) -}}
@DRUMANDBYTES.md
{{ end -}}
@RTK.md
14 changes: 14 additions & 0 deletions dot_claude/DRUMANDBYTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Drumandbytes infra

Self-hosted oracle-x64 Docker builds use driver: docker and keep the local build cache; cleanup caps it with docker buildx prune --builder default --max-used-space 20GB, never system prune --all.

# New repo setup (drumandbytes org)

When creating a new repo under the `drumandbytes` org, consider adding Dependabot (`.github/dependabot.yml`) and, if applicable, the `drumandbytes/reusable-actions` auto-merge workflow — as part of initial setup, not as a later retrofit. Applicable = the repo has real dependencies (a package ecosystem and/or pinned GitHub Actions) and a `CI` workflow for auto-merge's `workflow_run` trigger to hook into. Skip for static/no-dependency repos, issue trackers, and forks. If the repo uses `release-please` with the `dnb-robot` bot, auto-merge needs `patch-only-authors: '["dnb-robot[bot]"]'` and `use-app-token-for-merge: true` (see `eraser` or `music-router` for the exact shape); otherwise the reusable workflow's defaults need no extra inputs.

For every public-facing Drumandbytes website or app, add a normal crawlable link to `https://drumandbytes.com/projects/` in the global footer, About panel, or equivalent persistent site chrome. Use descriptive text such as “Drumandbytes Projects” or “Part of Drumandbytes”; do not add the link to authenticated or distraction-free screens where the product intentionally hides global navigation. For public GitHub repositories, ensure the README has a prominent live-site or project-hub link near the top when useful; do not add boilerplate backlinks to every README footer solely for SEO.

When adding a new public Drumandbytes site/hostname or a Cloudflare tunnel hostname, also (both lists are hand-maintained and drift silently):
- Add its route to `cloudflare-workers/ref-logger/wrangler.toml` (explicit hosts only, never zone wildcards). If the host already has its own route Worker, call `logRef()` there with the `dnb_refs` Analytics Engine binding instead (as `quantum-de-plan` and Mystery-Game do).
- Add tunnel hostnames to the host lists in `cluster/apps/grafana-dashboards/dashboards/cloudflare-tunnel.json` (talos = `proxmox-opentofu` `tunnel_services`; ghost-hetzner = `dnb-cloudflare-tf` CNAMEs to `<tunnel-id>.cfargotunnel.com`).
- Links to it from GitHub READMEs get `?ref=<repo>-readme`, hidden behind link text; never put `?ref=` in a repo's About-sidebar website URL (it's shown verbatim).
12 changes: 12 additions & 0 deletions dot_claude/SHELL.md.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Commands I paste into my shell

My interactive zsh (chezmoi `dot_zsh/aliases.zsh.tmpl`) aliases standard tools to replacements with different flags: `grep`→`rg`, `find`→`fd`, `ls`/`tree`→`eza`, `du`→`dust`, `df`→`duf`, `dig`→`doggo`, `man`→`tldr`, `kubectl`→`kubecolor`. Global aliases `G`/`H`/`T`/`L`/`CJ` expand to `| grep`/`| head`/`| tail`/`| less`/`| jid`. Your own Bash tool runs non-interactively, so this only affects commands you give me to run: write them in rg/fd syntax, or prefix `command` (e.g. `| command grep -E ...`) when POSIX flags are needed. `cat`, `sed`, `awk`, `curl`, `diff`, `rm`, `ps` are the real tools.

Shell functions (chezmoi `dot_zsh/functions.zsh.tmpl`) for managing the machine — suggest these instead of raw commands:
- Packages: plain `brew install` is intercepted (y/N nag) because installs should be tracked in the chezmoi Brewfile. To add one: `pkg-add <category> <pkg> [brew|cask] [--always]` (edits Brewfile + dots-setup registry, commits); taps: `tap-add <category> <owner/tap> <pkg> [brew|cask]`; remove: `pkg-rm <pkg>`; list categories: `pkg-cats`; new category: `pkg-group-add`. `brewsync` reconciles installed vs Brewfile drift. Drumandbytes casks (eraser, omniroute) live in the `dev_apps` category via the already-trusted `drumandbytes/tap`.
- Updates: `brew-up` (update + greedy upgrade + cleanup + regenerate tool inits); `mnt` = full maintenance (sync dotfiles from origin, brew-up, sheldon, recompile zsh, backup, reload).
- Other tracked installs: `mise-add <tool> [ver]` (runtimes), `uv-add <pkg>` (Python CLIs), `sh-add <user/repo>` (sheldon plugin), `comp-add <tool>` (completions) — each persists to chezmoi.
{{ if not (has "keepassxc" .machine_excludes) -}}
- Secrets: `kp-env <name>` prints `export` lines from the KeePassXC `env/<name>` group (Touch ID via kpxc-cli); repos wrap it as `just kp <recipe>`. Never ask me to paste tokens.
{{- end }}
- Dotfiles: `dots-diff` / `dots-apply` / `dots-status` / `dots-add` / `dots-edit` / `dots-push` (chezmoi). `theme light|dark` forces the theme across tools.
130 changes: 130 additions & 0 deletions dot_claude/executable_statusline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/bin/bash
# Custom Claude Code statusline: model, cwd, git branch/dirty/ahead-behind,
# context-window usage %, session cost/duration. Appends the RTK gain badge
# (real measured token savings from rtk's own log, not an estimate).

INPUT=$(cat)

DIM='\033[2m'
CYAN='\033[38;5;73m'
YELLOW='\033[38;5;179m'
RED='\033[38;5;167m'
GREEN='\033[38;5;108m'
RESET='\033[0m'

MODEL=$(printf '%s' "$INPUT" | jq -r '.model.display_name // "?"')
DIR=$(printf '%s' "$INPUT" | jq -r '.workspace.current_dir // .cwd // "."')
DIRNAME=$(basename "$DIR")

GIT_PART=""
if git -C "$DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
[ -z "$BRANCH" ] && BRANCH="detached"

DIRTY=""
if [ -n "$(git -C "$DIR" status --porcelain 2>/dev/null)" ]; then
DIRTY="*"
fi

AHEAD_BEHIND=""
read -r BEHIND AHEAD <<< "$(git -C "$DIR" rev-list --left-right --count '@{upstream}...HEAD' 2>/dev/null)"
if [ -n "$AHEAD" ] || [ -n "$BEHIND" ]; then
[ "$AHEAD" != "0" ] && [ -n "$AHEAD" ] && AHEAD_BEHIND="${AHEAD_BEHIND}↑${AHEAD}"
[ "$BEHIND" != "0" ] && [ -n "$BEHIND" ] && AHEAD_BEHIND="${AHEAD_BEHIND}↓${BEHIND}"
fi

GIT_PART=" ${DIM}|${RESET} ${CYAN}${BRANCH}${DIRTY}${RESET}"
[ -n "$AHEAD_BEHIND" ] && GIT_PART="${GIT_PART} ${DIM}${AHEAD_BEHIND}${RESET}"
fi

INPUT_TOKENS=$(printf '%s' "$INPUT" | jq -r '.context_window.total_input_tokens // empty')
CTX_USED=$(printf '%s' "$INPUT" | jq -r '.context_window.used_percentage // empty')
if [ -z "$CTX_USED" ]; then
WINDOW_SIZE=$(printf '%s' "$INPUT" | jq -r '.context_window.context_window_size // empty')
if [ -n "$INPUT_TOKENS" ] && [ -n "$WINDOW_SIZE" ] && [ "$WINDOW_SIZE" != "0" ]; then
CTX_USED=$(awk -v u="$INPUT_TOKENS" -v s="$WINDOW_SIZE" 'BEGIN{printf "%.0f", (u/s)*100}')
fi
fi

CTX_PART=""
if [ -n "$CTX_USED" ]; then
CTX_COLOR="$GREEN"
if [ "$CTX_USED" -ge 80 ] 2>/dev/null; then
CTX_COLOR="$RED"
elif [ "$CTX_USED" -ge 50 ] 2>/dev/null; then
CTX_COLOR="$YELLOW"
fi
CTX_PART=" ${DIM}|${RESET} ${CTX_COLOR}ctx ${CTX_USED}%${RESET}"
fi

# Checkpoint reminder: tiered on ABSOLUTE input tokens, not % of window.
# With a 1M window, % is a bad proxy for cost -- every turn resends the
# full token count regardless of how big the ceiling is.
if [ -n "$INPUT_TOKENS" ]; then
if [ "$INPUT_TOKENS" -ge 300000 ] 2>/dev/null; then
CTX_PART="${CTX_PART} ${RED}CHECKPOINT NOW${RESET}"
elif [ "$INPUT_TOKENS" -ge 150000 ] 2>/dev/null; then
CTX_PART="${CTX_PART} ${YELLOW}checkpoint soon${RESET}"
elif [ "$INPUT_TOKENS" -ge 50000 ] 2>/dev/null; then
CTX_PART="${CTX_PART} ${DIM}chk?${RESET}"
fi
fi

# Haiku 5.5 bills a prompt at the long rate once it passes 100K input tokens
# (cache read $0.01 -> $0.05 per M). Prompt size = this request's full input,
# not the session total, so prefer current_usage and fall back to the total.
PROMPT_TOKENS=$(printf '%s' "$INPUT" | jq -r '
.context_window.current_usage as $u
| if $u then (($u.input_tokens // 0) + ($u.cache_read_input_tokens // 0) + ($u.cache_creation_input_tokens // 0))
else (.context_window.total_input_tokens // empty) end')
MODEL_ID=$(printf '%s' "$INPUT" | jq -r '.model.id // ""')
HAIKU_PART=""
if [[ "${MODEL_ID} ${MODEL}" == *[Hh]aiku* ]] && [ -n "$PROMPT_TOKENS" ] 2>/dev/null; then
if [ "$PROMPT_TOKENS" -gt 100000 ] 2>/dev/null; then
HAIKU_COST=$(awk -v t="$PROMPT_TOKENS" 'BEGIN{printf "%.3f", t*0.05/1000000}')
HAIKU_PART=" ${RED}>100k 5x${RESET} ${DIM}~\$${HAIKU_COST}/turn cached${RESET}"
elif [ "$PROMPT_TOKENS" -ge 80000 ] 2>/dev/null; then
HAIKU_PART=" ${YELLOW}100k in $(( 100000 - PROMPT_TOKENS ))${RESET}"
fi
fi

COST=$(printf '%s' "$INPUT" | jq -r '.cost.total_cost_usd // empty')
COST_PART=""
if [ -n "$COST" ]; then
COST_FMT=$(awk -v c="$COST" 'BEGIN{printf "%.2f", c}')
COST_PART=" ${DIM}|${RESET} \$${COST_FMT}"
fi

DURATION_MS=$(printf '%s' "$INPUT" | jq -r '.cost.total_duration_ms // empty')
DUR_PART=""
if [ -n "$DURATION_MS" ]; then
DUR_MIN=$(awk -v d="$DURATION_MS" 'BEGIN{printf "%.0f", d/60000}')
DUR_PART=" ${DIM}${DUR_MIN}m${RESET}"
fi

LINES_ADDED=$(printf '%s' "$INPUT" | jq -r '.cost.total_lines_added // empty')
LINES_REMOVED=$(printf '%s' "$INPUT" | jq -r '.cost.total_lines_removed // empty')
LINES_PART=""
if [ -n "$LINES_ADDED" ] || [ -n "$LINES_REMOVED" ]; then
LINES_PART=" ${DIM}|${RESET} ${GREEN}+${LINES_ADDED:-0}${RESET} ${RED}-${LINES_REMOVED:-0}${RESET}"
fi

printf "${CYAN}[%s]${RESET} ${DIM}%s${RESET}%b%b%b%b%b%b" \
"$MODEL" "$DIRNAME" "$GIT_PART" "$CTX_PART" "$HAIKU_PART" "$COST_PART" "$DUR_PART" "$LINES_PART"

if command -v rtk >/dev/null 2>&1; then
RTK_JSON=$(rtk gain --format json 2>/dev/null)
RTK_SAVED=$(printf '%s' "$RTK_JSON" | jq -r '.summary.total_saved // empty' 2>/dev/null)
if [ -n "$RTK_SAVED" ]; then
RTK_PCT=$(printf '%s' "$RTK_JSON" | jq -r '.summary.avg_savings_pct // 0')
RTK_SAVED_FMT=$(awk -v n="$RTK_SAVED" 'BEGIN{
if (n >= 1000000) printf "%.1fM", n/1000000;
else if (n >= 1000) printf "%.1fk", n/1000;
else printf "%d", n;
}')
RTK_PCT_FMT=$(awk -v p="$RTK_PCT" 'BEGIN{printf "%.0f", p}')
printf ' %b|%b ⚡ %s (%s%%)' "$DIM" "$RESET" "$RTK_SAVED_FMT" "$RTK_PCT_FMT"
fi
fi

printf '\n'
40 changes: 40 additions & 0 deletions dot_claude/hooks/executable_bulk-read-guard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""PreToolUse(Read): block whole-file reads of big text files in the main agent.

Subagents (hook input carries agent_id) are exempt, so delegating to one works.
Threshold: BULK_READ_MIN_LINES env, default 400.
"""
import json
import os
import sys

SKIP_EXT = {".pdf", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ipynb", ".svg"}

try:
data = json.load(sys.stdin)
except ValueError:
sys.exit(0)

if data.get("agent_id"):
sys.exit(0)

inp = data.get("tool_input") or {}
path = inp.get("file_path", "")
if inp.get("offset") or inp.get("limit") or os.path.splitext(path)[1].lower() in SKIP_EXT:
sys.exit(0)

try:
with open(path, "rb") as f:
lines = sum(chunk.count(b"\n") for chunk in iter(lambda: f.read(1 << 20), b""))
except OSError:
sys.exit(0)

limit = int(os.environ.get("BULK_READ_MIN_LINES", "400"))
if lines > limit:
print(
f"Blocked: {path} has {lines} lines (> {limit}). To understand/summarize it, "
"delegate to an Explore subagent (model haiku) with the question. "
"To edit a specific region, re-Read with offset/limit.",
file=sys.stderr,
)
sys.exit(2)
Loading
Loading