diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 17bb1449..b55b2bd4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,8 @@ name: Formatting checks -on: [push] +on: + push: + pull_request: env: TERM: xterm @@ -10,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 with: fetch-depth: 1 diff --git a/.gitignore b/.gitignore index 73ef0a93..884e25ae 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,6 @@ vim/log vim/plugged .netrwhist .idea -offlineimap/Account* -offlineimap/Repository* docker __pycache__ .DS_Store diff --git a/Makefile b/Makefile index 44cf052e..06e49a66 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,28 @@ +YAML_FILES := $(shell git ls-files '*.yml' '*.yaml' | while IFS= read -r file; do [ -f "$$file" ] && printf "%s " "$$file"; done) +MARKDOWN_FILES := $(shell git ls-files '*.md' | while IFS= read -r file; do [ -f "$$file" ] && printf "%s " "$$file"; done) +PYTHON_FILES := $(shell git ls-files '*.py' | while IFS= read -r file; do [ -f "$$file" ] && printf "%s " "$$file"; done) +LUA_FILES := $(shell git ls-files '*.lua' | while IFS= read -r file; do [ -f "$$file" ] && printf "%s " "$$file"; done) + +.PHONY: all apply fmt fmt_check nvim-health nvim-check + all: fmt apply nvim-check apply: uv run ansible-playbook -i ~/.dotfiles/ansible/inventory.ini ~/.dotfiles/ansible/dotfiles.yml fmt: - npx --loglevel error --yes prettier --write **/*.yml - uvx --with mdformat-gfm --with mdformat-frontmatter mdformat --wrap 80 --number **/*.md - uvx ruff format --line-length=100 **/*.py - uvx ruff check --fix --line-length=100 **/*.py - npx --loglevel error --yes @johnnymorganz/stylua-bin -- **/*.lua + @if [ -n "$(YAML_FILES)" ]; then npx --loglevel error --yes prettier --write $(YAML_FILES); fi + @if [ -n "$(MARKDOWN_FILES)" ]; then uvx --with mdformat-gfm --with mdformat-frontmatter mdformat --wrap 80 --number $(MARKDOWN_FILES); fi + @if [ -n "$(PYTHON_FILES)" ]; then uvx ruff format --line-length=100 $(PYTHON_FILES); fi + @if [ -n "$(PYTHON_FILES)" ]; then uvx ruff check --fix --line-length=100 $(PYTHON_FILES); fi + @if [ -n "$(LUA_FILES)" ]; then npx --loglevel error --yes @johnnymorganz/stylua-bin -- $(LUA_FILES); fi fmt_check: - npx --loglevel error --yes prettier --check **/*.yml - uvx --with mdformat-gfm --with mdformat-frontmatter mdformat --check --wrap 80 --number **/*.md - uvx ruff format --check --line-length=100 **/*.py - uvx ruff check --line-length=100 **/*.py - npx --loglevel error --yes @johnnymorganz/stylua-bin --check -- **/*.lua + @if [ -n "$(YAML_FILES)" ]; then npx --loglevel error --yes prettier --check $(YAML_FILES); fi + @if [ -n "$(MARKDOWN_FILES)" ]; then uvx --with mdformat-gfm --with mdformat-frontmatter mdformat --check --wrap 80 --number $(MARKDOWN_FILES); fi + @if [ -n "$(PYTHON_FILES)" ]; then uvx ruff format --check --line-length=100 $(PYTHON_FILES); fi + @if [ -n "$(PYTHON_FILES)" ]; then uvx ruff check --line-length=100 $(PYTHON_FILES); fi + @if [ -n "$(LUA_FILES)" ]; then npx --loglevel error --yes @johnnymorganz/stylua-bin --check -- $(LUA_FILES); fi nvim-health: nvim --headless "+checkhealth" +qa diff --git a/README.md b/README.md index 1200b309..07ae9741 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ My personal dotfiles for macOS, managed with Ansible. -## Ansible +## Quick start ```bash # Clone the repository @@ -10,35 +10,28 @@ git clone https://github.com/michaelbarton/dotfiles.git ~/.dotfiles # Apply configuration with Ansible cd ~/.dotfiles -./ansible/apply_ansible +make apply ``` -### Fish Shell +## Run specific Ansible tags ```bash -curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher -fish -c "fisher update" +uv run ansible-playbook -i ~/.dotfiles/ansible/inventory.ini ~/.dotfiles/ansible/dotfiles.yml --tags "hooks,setup" ``` -### Email Setup - -1. Create maildb directory: +## Maintenance - ```bash - mkdir -p ~/.maildb/michaelbarton - ``` - -2. Set up Gmail password in keychain: - - ```bash - security add-generic-password -a acct.gmail -s acct.gmail -w - ``` +```bash +make fmt_check +make nvim-check +``` -3. Set up automated email sync: +### Fish Shell - ```bash - launchctl load -w ~/Library/LaunchAgents/uk.me.michaelbarton.offlineimap.plist - ``` +```bash +curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher +fish -c "fisher update" +``` ## Customization diff --git a/Rprofile b/Rprofile deleted file mode 100644 index b68b3787..00000000 --- a/Rprofile +++ /dev/null @@ -1,8 +0,0 @@ -options( "device" = "quartz" ) - -setHook(packageEvent("grDevices", "onLoad"), - function(...) grDevices::quartz.options(width=4, height=4)) - -options(prompt="R> ", digits=4, show.signif.stars=FALSE) - -options(repos=structure(c(CRAN="https://cloud.r-project.org/"))) diff --git a/ansible/dotfiles.yml b/ansible/dotfiles.yml index 6bc41b99..43296e50 100644 --- a/ansible/dotfiles.yml +++ b/ansible/dotfiles.yml @@ -6,8 +6,9 @@ # - Configuring OS-specific launch agents # - Linking dotfiles to their proper locations # -# To run only specific tags: ./apply_ansible [tag1,tag2] -# Available tags: links, python, agents +# To run only specific tags: +# uv run ansible-playbook -i ~/.dotfiles/ansible/inventory.ini ~/.dotfiles/ansible/dotfiles.yml --tags "hooks,setup" +# Common tags: links, nvim, tmux, skills, hooks, setup, agents, paperless, paperless-backup - name: Playbook to manage dotfiles hosts: localhost @@ -32,6 +33,7 @@ - { path: "{{ config_dir }}/atuin" } - { path: "{{ config_dir }}/lazygit" } - { path: "{{ config_dir }}/ripgrep" } + - { path: "{{ config_dir }}/uv" } - { path: "{{ ansible_user_dir }}/cache" } - { path: "{{ ansible_user_dir }}/.bin" } - { path: "{{ ansible_user_dir }}/.hammerspoon" } diff --git a/ansible/tasks/link_files.yml b/ansible/tasks/link_files.yml index 767ccd05..0065206d 100644 --- a/ansible/tasks/link_files.yml +++ b/ansible/tasks/link_files.yml @@ -24,6 +24,7 @@ - { src: "atuin/config.toml", dest: ".config/atuin/config.toml" } - { src: "lazygit/config.yml", dest: ".config/lazygit/config.yml" } - { src: "starship/starship.toml", dest: ".config/starship.toml" } + - { src: "uv/uv.toml", dest: ".config/uv/uv.toml" } # Scripts for working with data - { src: "parquet/csv_to_parquet.py", dest: ".bin/csv_to_parquet" } @@ -32,6 +33,9 @@ # Misc. scripts and config - { src: "zettel/today.sh", dest: ".bin/today" } - { src: "git/gitconfig", dest: ".gitconfig" } + - { src: "gitignore", dest: ".gitignore" } + - { src: "duckdb/duckdbrc", dest: ".duckdbrc" } + - { src: "visidata/visidatarc", dest: ".visidatarc" } - { src: "markdown/md2docx.sh", dest: ".bin/md2docx" } - { src: "poetry/update_poetry_dependencies.py", diff --git a/ansible/tasks/paperless_backup.yml b/ansible/tasks/paperless_backup.yml index 46cc0adb..9e6c581f 100644 --- a/ansible/tasks/paperless_backup.yml +++ b/ansible/tasks/paperless_backup.yml @@ -21,7 +21,6 @@ cmd: launchctl load "{{ ansible_user_dir }}/Library/LaunchAgents/uk.me.michaelbarton.paperless-backup.plist" args: creates: "{{ ansible_user_dir }}/Library/LaunchAgents/uk.me.michaelbarton.paperless-backup.plist" - # One-time manual setup required (run these once before first backup): # # security add-generic-password -a $USER -s paperless-backup-b2-id -w '' diff --git a/bash/bashrc b/bash/bashrc index 2da82bac..d032c3c4 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -1,6 +1,8 @@ #!/bin/bash -set -o nounset +if [ -n "${BASH_VERSION:-}" ]; then + set -o nounset +fi ################################################################### # @@ -57,11 +59,10 @@ alias grep='grep --color=auto' PYTHON_BIN=${HOME}/.venv/bin USER_BIN=${HOME}/.bin LOCAL_BIN=${HOME}/.local/bin -DOTFILES_BIN=${HOME}/.dotfiles/bin HOMEBREW_BIN=/opt/homebrew/bin NPM_BIN=${HOME}/.npm-global/bin -export PATH=${NPM_BIN}:${HOMEBREW_BIN}:${DOTFILES_BIN}:${USER_BIN}:${LOCAL_BIN}:${PATH} +export PATH=${NPM_BIN}:${HOMEBREW_BIN}:${USER_BIN}:${LOCAL_BIN}:${PATH} ################################################################### # @@ -69,9 +70,14 @@ export PATH=${NPM_BIN}:${HOMEBREW_BIN}:${DOTFILES_BIN}:${USER_BIN}:${LOCAL_BIN}: # ################################################################### -# fzf should use ripgrep for search -export FZF_DEFAULT_COMMAND='rg --files' -export FZF_DEFAULT_OPTS='-m --height 50% --border' +# fzf and ripgrep search defaults +if command -v fd >/dev/null 2>&1; then + export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git' +else + export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"' +fi +export FZF_DEFAULT_OPTS='--height 50% --border' +export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/rc" export MANWIDTH=80 export EDITOR=nvim @@ -107,7 +113,7 @@ export LANG='en_GB.UTF-8' # ################################################################### -if [ -n "$BASH_VERSION" ]; then +if [ -n "${BASH_VERSION:-}" ]; then # Shell history improvements export HISTSIZE=50000 export HISTFILESIZE=100000 diff --git a/claude/hooks/plan-review.sh b/claude/hooks/plan-review.sh index 85bab5a4..0e88bd4b 100755 --- a/claude/hooks/plan-review.sh +++ b/claude/hooks/plan-review.sh @@ -31,21 +31,32 @@ if [ ! -f "$planning_rule" ]; then exit 1 fi -# Strip the YAML frontmatter (between the first two `---` lines). -rule_body=$(awk 'BEGIN{f=0} /^---$/{f++; next} f>=2{print}' "$planning_rule") +# The gate list is the single source of truth: it lives in the rule as a +# `` comment (invisible in Cursor's rendered view) and is +# extracted here rather than duplicated, so the two can never drift. +gates=$(awk '/^$/{f=0} f' "$planning_rule") +if [ -z "$gates" ]; then + echo "plan-review hook: no gate manifest () in $planning_rule" >&2 + exit 1 +fi + +# Strip the YAML frontmatter (between the first two `---` lines) and the gate +# manifest comment, leaving the human-readable rule to inject verbatim. +rule_body=$(awk ' + BEGIN{f=0} + /^---$/{f++; next} + f<2{next} + /^$/{if(s){s=0; next}} + !s{print} +' "$planning_rule") prompt=$( cat < below. The gates are: -1. Success & exit criteria -2. Invariants -3. What is the grain of the data -4. Failure modes (including the one-line premortem) -5. Assumptions & unknowns -6. Minimal viable change -7. Visualization confirmation (only when the plan involves figures) +$gates A gate passes ONLY if you can quote the plan line(s) that satisfy it. Paraphrases, "implied somewhere," or "this is obvious" do NOT count. diff --git a/claude/settings.json b/claude/settings.json index bf317b4c..c9f8ecdf 100644 --- a/claude/settings.json +++ b/claude/settings.json @@ -7,6 +7,7 @@ ], "defaultMode": "auto" }, + "model": "sonnet", "hooks": { "PostToolUse": [ { @@ -47,6 +48,7 @@ } ] }, + "effortLevel": "high", "tui": "fullscreen", "theme": "auto", "skipAutoPermissionPrompt": true diff --git a/cursor/README.md b/cursor/README.md index 58891a83..e6c35618 100644 --- a/cursor/README.md +++ b/cursor/README.md @@ -15,7 +15,7 @@ ln -sf ~/.dotfiles/cursor/rules ~/.cursor/rules Or run Ansible with the `hooks` or `setup` tag: ```bash -./apply_ansible hooks +uv run ansible-playbook -i ~/.dotfiles/ansible/inventory.ini ~/.dotfiles/ansible/dotfiles.yml --tags hooks ``` ### Cursor: enable third-party hooks diff --git a/cursor/rules/CHANGELOG.md b/cursor/rules/CHANGELOG.md new file mode 100644 index 00000000..301447d8 --- /dev/null +++ b/cursor/rules/CHANGELOG.md @@ -0,0 +1,117 @@ +# Changelog — agent planning/dbt rules + +Rationale log for `cursor/rules/planning.mdc` and `cursor/rules/dbt.mdc`. + +Git records *what* changed line-by-line; this file records *why*, and the +alternatives considered and rejected — so a later edit doesn't silently +re-litigate a settled decision. Newest first. Each entry: What / Why / +Considered & rejected. + +______________________________________________________________________ + +## 2026-07-14 — Make the plan-review gate list single-source + +**What changed** + +- The audit gate list now lives only in `planning.mdc`, as a + `` HTML comment (invisible in Cursor's rendered view). +- `claude/hooks/plan-review.sh` extracts that manifest at runtime and injects it + in place of its former hardcoded numbered list; it also strips the comment + from the rule body it injects, and fails loud (`exit 1`) if the manifest is + missing. + +**Why** + +- The gate list was duplicated in two files that had to be edited together by + hand; the premortem-reframing change above required exactly that dual edit. + Nothing caught a mismatch, so a future rule edit could leave the hook auditing + against a stale rubric. Deriving the list from the rule makes drift + structurally impossible. + +**Considered & rejected** + +- *A CI/`make check` that detects divergence* — rejected: it would need to + normalize the two deliberately-different naming schemes, and the repo has no + pre-commit or shell-test harness. Eliminating the second copy is less + infrastructure and strictly more reliable than detecting drift after the fact. + +______________________________________________________________________ + +## 2026-07-14 — Reframe premortem for analysis work; promote Context & Verification + +**What changed** + +- Split the Premortem into two framings chosen by what the work produces: + analysis/report → "this result was shared and the conclusion turned out to be + wrong because …" (with analysis-specific failure classes); maintained + pipeline/tool/config → the original "it is 3 months later …". +- Promoted **Context** to a required first section. +- Added **Verification** as its own section (reproduce commands, distinct from + the done-checklist). +- Synced `claude/hooks/plan-review.sh` gate list to match (now 9 gates). + +**Why** + +- Evidence: survey of all 54 saved plans in `~/.claude/plans/` (2026-06-15 → + 2026-07-14) plus 216 Claude Code session transcripts. ~80% of the operator's + work is one-off analysis, data investigation, and report authoring — not + maintained software — so the "3 months later" premortem framing mismatched the + common case. The failure that actually bites is a wrong conclusion escaping + into a report/Slack/validation doc. +- Context appeared in 87% of plans and Verification in 72% despite neither being + mandated — codifying what plans already do, not adding ceremony. +- The template otherwise showed no friction: grain/fan-out gates catch real bugs + and the viz/failure gates self-suppress via honest `N/A`. + +**Considered & rejected** + +- *Work-type "mode selector" at the top of every plan* — rejected as + over-templating; the existing `N/A` self-suppression already degrades + gracefully across work types, with no observed friction. +- *Relaxing the 60-second-review rule* — rejected. Plans do run long (median + ~200 lines) but transcripts show zero complaint, and loosening the rule would + only license more length. +- *Reintroducing "Outside view"* — rejected; it reappeared organically in ~24% + of plans but was deliberately cut in #98, so not re-adding without an explicit + ask. + +______________________________________________________________________ + +## 2026-06-28 — Replace "Outside view" with Success & grain gates (#98) + +*(reconstructed from git history)* + +**What changed** + +- Renamed `Exit criteria` → `Success & exit criteria` (added a one-sentence + end-state before the done checklist). +- Promoted grain from a sub-bullet inside Invariants to its own standalone gate, + "What is the grain of the data". +- Dropped the "Outside view" gate (reference-class effort/risk estimation with a + buffer multiplier). + +**Why** + +- Grain was getting buried inside Invariants where it was easy to skip; as the + spec the operator reviews, it needed to stand on its own. +- Outside-view effort estimation wasn't earning its keep in analytical/dbt work. + +______________________________________________________________________ + +## 2026-06-12 — Introduce rule + hook system (#94) + +*(reconstructed from git history)* + +**What changed** + +- Introduced `planning.mdc` and the deployment/hook system: Cursor rules symlink + and Claude Code PostToolUse/Stop hooks. +- Made `planning.mdc` the single source of truth: the hook substitutes its body + rather than duplicating the gates in a separate template; dropped an initial + `claude/prompts/plan-review.md` (Claude Code has no `prompts/` convention). + +**Why / Considered & rejected** + +- Dropped production-engineering gates as ill-suited to analytical/dbt work: + **Reversibility**, **System 2 triggers**, and **three-tier boundaries for + actions** (with a shop-specific "Never do" list). diff --git a/cursor/rules/planning.mdc b/cursor/rules/planning.mdc index 29cfdfe6..28fddbe9 100644 --- a/cursor/rules/planning.mdc +++ b/cursor/rules/planning.mdc @@ -3,10 +3,23 @@ description: Planning and task execution rules alwaysApply: true --- + + ## Planning Every plan MUST include these sections (bullets, reviewable in under 60 seconds): +- **Context**: One or two sentences — why this work, what prompted it, the root cause or question being answered. This is the first thing the operator reads. - **Success & exit criteria**: One sentence on the desired end state (user-visible or system-observable outcome), followed by the done checklist: - Build succeeds (e.g. `just build`, `dbt build`) - Rendered output is correct if applicable (e.g. `quarto render`) @@ -24,8 +37,11 @@ Every plan MUST include these sections (bullets, reviewable in under 60 seconds) - "join fan-out from a 1:N relationship" - "grain change in intermediate breaks downstream" - "database lock from stale process" - - **Premortem** (one line): "It is 3 months later and this failed because …" (top 2–3 concrete reasons) + - **Premortem** (one line), framed by what the work produces: + - *Analysis, investigation, or a data-backed report* (the common case): "This result was shared and the conclusion turned out to be wrong because …" (top 2–3 concrete reasons). Look first at the failure classes analysis actually produces: join fan-out / double counting, wrong denominator, the analyzed population not matching the stated universe, stale or partial input data, misread column semantics, a plausible number nobody spot-checked against a known case. + - *A pipeline, model, tool, or config that will be maintained*: "It is 3 months later and this failed because …" (top 2–3 concrete reasons) - **Assumptions & unknowns**: What we treat as true but have not verified; what this plan does *not* cover; what would invalidate the plan ("if X is wrong, we stop") +- **Verification**: The exact commands to reproduce/observe the result end-to-end (e.g. the `duckdb` / `grep` / `quarto render` invocation and what output confirms success). Distinct from the done-checklist: this is the runbook, not the acceptance test. ### Minimal viable change diff --git a/duckdb/duckdbrc b/duckdb/duckdbrc new file mode 100644 index 00000000..e9cba6b8 --- /dev/null +++ b/duckdb/duckdbrc @@ -0,0 +1,3 @@ +.nullvalue ∅ +.maxwidth 0 +.mode duckbox diff --git a/fish/config.fish b/fish/config.fish index 2851a080..a2e4ad34 100644 --- a/fish/config.fish +++ b/fish/config.fish @@ -4,20 +4,22 @@ set -x XDG_DATA_HOME $HOME/.local/share set -x XDG_CACHE_HOME $HOME/.cache set -x XDG_STATE_HOME $HOME/.local/state -# --- Cursor Terminal Fix --- -# The complex prompt from starship can cause the terminal in Cursor (which is -# based on VS Code) to hang, as it has trouble determining when a command -# has finished executing. -# To fix this, we detect if we are running inside Cursor's terminal by -# checking if TERM_PROGRAM is "vscode". If it is, we use a simple, minimal -# prompt. Otherwise, we use the full starship prompt. -# This ensures a stable experience in Cursor without sacrificing the rich -# prompt in other terminal emulators. -# - -# +# --- Cursor terminal prompt safety --- +# Keep Cursor terminals stable by avoiding starship there, but still load the +# rest of the fish environment (aliases, PATH, functions, etc). if test "$TERM_PROGRAM" = vscode - exit 0 + function fish_prompt + set_color cyan + echo -n (prompt_pwd) " > " + set_color normal + end + + function fish_right_prompt + end +else + if command -v starship &>/dev/null + starship init fish | source + end end # Auto-attach to tmux in Ghostty (but not inside nvim, scripts, or existing tmux) @@ -25,9 +27,10 @@ if test "$TERM_PROGRAM" = ghostty; and not set -q TMUX; and not set -q NVIM; and tmux new-session -A -s main end -# Initialize starship and zoxide -starship init fish | source -zoxide init fish | source +# Initialize zoxide if installed +if command -v zoxide &>/dev/null + zoxide init fish | source +end # Initialize mise for runtime version management (if installed) if command -v mise &>/dev/null @@ -81,20 +84,22 @@ alias grep='grep --color=auto' set -x PYTHON_BIN $HOME/.venv/bin set -x USER_BIN $HOME/.bin set -x LOCAL_BIN $HOME/.local/bin -set -x DOTFILES_BIN $HOME/.dotfiles/bin set -x HOMEBREW_BIN /opt/homebrew/bin set -x NPM_BIN $HOME/.npm-global/bin # Prepend paths to PATH (fish uses fish_add_path for this) fish_add_path $NPM_BIN fish_add_path $HOMEBREW_BIN -fish_add_path $DOTFILES_BIN fish_add_path $USER_BIN fish_add_path $LOCAL_BIN fish_add_path $PYTHON_BIN # FZF configuration with better preview -set -x FZF_DEFAULT_COMMAND 'fd --type f --hidden --follow --exclude .git' +if command -v fd &>/dev/null + set -x FZF_DEFAULT_COMMAND 'fd --type f --hidden --follow --exclude .git' +else + set -x FZF_DEFAULT_COMMAND 'rg --files --hidden --glob !.git' +end # --tmux opens fzf as a tmux popup (silently ignored outside tmux) # bat --paging=never prevents bat spawning a nested pager in fzf preview set -x FZF_DEFAULT_OPTS '--tmux center,80%,70% --layout=reverse --preview "bat --style=numbers --color=always --paging=never --line-range :500 {}" --preview-window=right:60%:wrap' @@ -151,22 +156,16 @@ set -x LANG 'en_GB.UTF-8' # ################################################################### -# Only create aliases if coreutils is installed -# Check for coreutils by looking for one of its binaries instead of running brew list -if test -x /opt/homebrew/bin/gcat - # Use hardcoded path for M1/M2 Macs (adjust if on Intel Mac) - set brew_prefix /opt/homebrew - - # Only alias the most commonly used commands to reduce startup time - alias cp="$brew_prefix/bin/gcp" - alias date="$brew_prefix/bin/gdate" - alias echo="$brew_prefix/bin/gecho" - alias mv="$brew_prefix/bin/gmv" - alias rm="$brew_prefix/bin/grm" - alias sed="$brew_prefix/bin/gsed" - alias sort="$brew_prefix/bin/gsort" - alias tail="$brew_prefix/bin/gtail" - +# Only alias the most commonly used commands to reduce startup time. +# Check each g* binary individually (rather than gating on gcat alone) so a +# partial coreutils install doesn't silently skip every alias below it. +# Use hardcoded path for M1/M2 Macs (adjust if on Intel Mac) +set brew_prefix /opt/homebrew +for pair in cp:gcp date:gdate echo:gecho mv:gmv rm:grm sed:gsed sort:gsort tail:gtail + set -l parts (string split ":" $pair) + if test -x "$brew_prefix/bin/$parts[2]" + alias $parts[1]="$brew_prefix/bin/$parts[2]" + end end ################################################################### @@ -271,6 +270,24 @@ function qwatch kill $quarto_pid 2>/dev/null end +# Run tmp/scratch.sql against duckdb (default: in-memory, or pass a db path) +function dq + if not test -f tmp/scratch.sql + echo "dq: tmp/scratch.sql not found" + return 1 + end + duckdb $argv[1] < tmp/scratch.sql +end + +# Same as dq, but pipe CSV output into visidata +function dv + if not test -f tmp/scratch.sql + echo "dv: tmp/scratch.sql not found" + return 1 + end + duckdb -csv $argv[1] < tmp/scratch.sql | vd -f csv - +end + # LESS colors for man pages set -gx LESS_TERMCAP_us \e\[1\;32m set -gx LESS_TERMCAP_md \e\[1\;31m diff --git a/ghostty/config b/ghostty/config index 867f9d12..df251475 100644 --- a/ghostty/config +++ b/ghostty/config @@ -6,10 +6,10 @@ theme = Catppuccin Frappe command = /opt/homebrew/bin/fish # Auto-copy Ghostty terminfo to SSH remote hosts (prevents "unknown terminal" errors) -auto-install-terminfo = true +shell-integration-features = ssh-terminfo -# Desktop notification when a long-running command finishes -notify-on-command-finish = true +# Desktop notification when a long-running command finishes (only when window isn't focused) +notify-on-command-finish = unfocused # Let tmux handle all window/tab management keybind = super+t=unbind diff --git a/gitignore b/gitignore index 83275666..39ece709 100644 --- a/gitignore +++ b/gitignore @@ -259,4 +259,5 @@ coverage/ .sass-cache/ *.pid *.seed -*.pid.lock \ No newline at end of file +*.pid.lock +**/.claude/settings.local.json diff --git a/grafana/query_grafana.bash b/grafana/query_grafana.bash deleted file mode 100755 index 3b82f629..00000000 --- a/grafana/query_grafana.bash +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o xtrace - -CONTAINER_NAME=${1:?"Usage: $0 []"} -URL="http://localhost:3100/loki/api/v1/query_range" -START_TIME=$(date -u -v-24H +%s) -END_TIME=$(date -u +%s) -QUERY="{pipelineName=\"${CONTAINER_NAME}\"} | json | line_format \"{{.log}}\" | json | line_format \"{{.message}}\" |= \"level=\"" - -# Check if the second argument (additional_filter) is provided -if [ $# -gt 1 ]; then - ADDITIONAL_FILTER="|=\"${2}\"" - QUERY="${QUERY}${ADDITIONAL_FILTER}" -fi - -RESPONSE=$(curl --get "${URL}" \ - --data-urlencode "query=${QUERY}" \ - --data-urlencode "start=${START_TIME}" \ - --data-urlencode "end=${END_TIME}" \ - --data-urlencode "limit=1000") - -if [ "$(echo "$RESPONSE" | jq '.status')" != "\"success\"" ]; then - echo "Error: Query failed. Response was: $RESPONSE" >&2 - exit 1 -fi - -echo "$RESPONSE" | jq -r ".data.result | map(.stream) | flatten | map(.message) | .[]" diff --git a/hocon/preview_hocon b/hocon/preview_hocon deleted file mode 100755 index f8e2da6c..00000000 --- a/hocon/preview_hocon +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -set -o nounset -SRC=$1 - -cat $SRC | pyhocon -f json | jq diff --git a/mailcap b/mailcap deleted file mode 100644 index 58edbde8..00000000 --- a/mailcap +++ /dev/null @@ -1,2 +0,0 @@ -text/html; w3m -T text/html -dump %s; copiousoutput -application/pdf; pdftotext %s - | par s0p0; copiousoutput diff --git a/msmtp/linux.rc b/msmtp/linux.rc deleted file mode 100644 index a9d1d179..00000000 --- a/msmtp/linux.rc +++ /dev/null @@ -1,13 +0,0 @@ -# Set default values for all following accounts. -defaults -auth on -tls on - -# Gmail -account gmail -host smtp.gmail.com -port 587 -protocol smtp -from mail@michaelbarton.me.uk -user mail@michaelbarton.me.uk -passwordeval secret-tool lookup password gmail diff --git a/msmtp/osx.rc b/msmtp/osx.rc deleted file mode 100644 index d806e59e..00000000 --- a/msmtp/osx.rc +++ /dev/null @@ -1,14 +0,0 @@ -# Set default values for all following accounts. -defaults -auth on -tls on - -# Gmail -account gmail -host smtp.gmail.com -port 587 -protocol smtp -from mail@michaelbarton.me.uk -user mail@michaelbarton.me.uk -# Change according to KeyChain entry name -passwordeval security find-generic-password -w -a acct.gmail diff --git a/mutt/accounts.muttrc b/mutt/accounts.muttrc deleted file mode 100644 index 8e088cb1..00000000 --- a/mutt/accounts.muttrc +++ /dev/null @@ -1,8 +0,0 @@ -set folder = $HOME/.maildb/michaelbarton -set spoolfile = "+INBOX" -folder-hook $HOME/.maildb/ "\ - set postponed = +'[Google Mail].Drafts' \ - record = +'[Google Mail].Sent Mail' \ - from = '$realname ' \ - sendmail = 'msmtp -a gmail' \ - sendmail_wait = 0 diff --git a/mutt/appearance.muttrc b/mutt/appearance.muttrc deleted file mode 100644 index d5f41a7c..00000000 --- a/mutt/appearance.muttrc +++ /dev/null @@ -1,10 +0,0 @@ -set sort_aux = 'last-date-received' -set index_format = "[%Z] %D %-20.20F %s" -set date_format = "%m/%d" -set nostrict_threads -set sort = 'threads' - -#set sidebar_width=26 -#set sidebar_visible=yes -#set sidebar_delim='|' -#mailboxes $HOME/.maildb/michaelbarton/Inbox $HOME/.maildb/jgi/Inbox $HOME/.maildb/michaelbarton/bioboxes $HOME/.maildb/michaelbarton/ci-server diff --git a/mutt/colors b/mutt/colors deleted file mode 120000 index 47815ac2..00000000 --- a/mutt/colors +++ /dev/null @@ -1 +0,0 @@ -/Users/michael.barton/.dotfiles/solarized/mutt-colors-solarized/mutt-colors-solarized-dark-256.muttrc \ No newline at end of file diff --git a/mutt/colors.muttrc b/mutt/colors.muttrc deleted file mode 100644 index b3186518..00000000 --- a/mutt/colors.muttrc +++ /dev/null @@ -1,151 +0,0 @@ -# vim: filetype=muttrc - -# -# -# make sure that you are using mutt linked against slang, not ncurses, or -# suffer the consequences of weird color issues. use "mutt -v" to check this. - -# custom body highlights ----------------------------------------------- -# highlight my name and other personally relevant strings -#color body color136 color234 "(ethan|schoonover)" -# custom index highlights ---------------------------------------------- -# messages which mention my name in the body -#color index color136 color234 "~b \"phil(_g|\!| gregory| gold)|pgregory\" !~N !~T !~F !~p !~P" -#color index J_cream color230 "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~N !~T !~F !~p !~P" -#color index color136 color37 "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~T !~F !~p !~P" -#color index color136 J_magent "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~F !~p !~P" -## messages which are in reference to my mails -#color index J_magent color234 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" !~N !~T !~F !~p !~P" -#color index J_magent color230 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~N !~T !~F !~p !~P" -#color index J_magent color37 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~T !~F !~p !~P" -#color index J_magent color160 "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~F !~p !~P" - -# for background in 16 color terminal, valid background colors include: -# base03, bg, black, any of the non brights - -# basic colors --------------------------------------------------------- -color normal color241 color234 -color error color160 color234 -color tilde color235 color234 -color message color37 color234 -color markers color160 color254 -color attachment color254 color234 -color search color61 color234 -#color status J_black J_status -color status color241 color235 -color indicator color234 color136 -color tree color136 color234 # arrow in threads - -# basic monocolor screen -mono bold bold -mono underline underline -mono indicator reverse -mono error bold - -# index ---------------------------------------------------------------- - -#color index color160 color234 "~D(!~p|~p)" # deleted -#color index color235 color234 ~F # flagged -#color index color166 color234 ~= # duplicate messages -#color index color240 color234 "~A!~N!~T!~p!~Q!~F!~D!~P" # the rest -#color index J_base color234 "~A~N!~T!~p!~Q!~F!~D" # the rest, new -color index color160 color234 "~A" # all messages -color index color166 color234 "~E" # expired messages -color index color33 color234 "~N" # new messages -color index color33 color234 "~O" # old messages -color index color61 color234 "~Q" # messages that have been replied to -color index color240 color234 "~R" # read messages -color index color33 color234 "~U" # unread messages -color index color33 color234 "~U~$" # unread, unreferenced messages -color index color241 color234 "~v" # messages part of a collapsed thread -color index color241 color234 "~P" # messages from me -color index color37 color234 "~p!~F" # messages to me -color index color37 color234 "~N~p!~F" # new messages to me -color index color37 color234 "~U~p!~F" # unread messages to me -color index color240 color234 "~R~p!~F" # messages to me -color index color160 color234 "~F" # flagged messages -color index color160 color234 "~F~p" # flagged messages to me -color index color160 color234 "~N~F" # new flagged messages -color index color160 color234 "~N~F~p" # new flagged messages to me -color index color160 color234 "~U~F~p" # new flagged messages to me -color index color235 color160 "~D" # deleted messages -color index color245 color234 "~v~(!~N)" # collapsed thread with no unread -color index color136 color234 "~v~(~N)" # collapsed thread with some unread -color index color64 color234 "~N~v~(~N)" # collapsed thread with unread parent -# statusbg used to indicated flagged when foreground color shows other status -# for collapsed thread -color index color160 color235 "~v~(~F)!~N" # collapsed thread with flagged, no unread -color index color136 color235 "~v~(~F~N)" # collapsed thread with some unread & flagged -color index color64 color235 "~N~v~(~F~N)" # collapsed thread with unread parent & flagged -color index color64 color235 "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged -color index color37 color235 "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly -color index color136 color160 "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial) -#color index color136 color234 "~(~N)" # messages in threads with some unread -#color index color64 color234 "~S" # superseded messages -#color index color160 color234 "~T" # tagged messages -#color index color166 color160 "~=" # duplicated messages - -# message headers ------------------------------------------------------ - -#color header color240 color234 "^" -color hdrdefault color240 color234 -color header color241 color234 "^(From)" -color header color33 color234 "^(Subject)" - -# body ----------------------------------------------------------------- - -color quoted color33 color234 -color quoted1 color37 color234 -color quoted2 color136 color234 -color quoted3 color160 color234 -color quoted4 color166 color234 - -color signature color240 color234 -color bold color235 color234 -color underline color235 color234 -color normal color244 color234 -# -color body color245 color234 "[;:][-o][)/(|]" # emoticons -color body color245 color234 "[;:][)(|]" # emoticons -color body color245 color234 "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\ - |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\ - |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?" -color body color245 color234 "[ ][*][^*]*[*][ ]?" # more emoticon? -color body color245 color234 "[ ]?[*][^*]*[*][ ]" # more emoticon? - -## pgp - -color body color160 color234 "(BAD signature)" -color body color37 color234 "(Good signature)" -color body color234 color234 "^gpg: Good signature .*" -color body color241 color234 "^gpg: " -color body color241 color160 "^gpg: BAD signature from.*" -mono body bold "^gpg: Good signature" -mono body bold "^gpg: BAD signature from.*" - -# yes, an insance URL regex -color body color160 color234 "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" -# and a heavy handed email regex -#color body J_magent color234 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])" - -# Various smilies and the like -#color body color230 color234 "<[Gg]>" # -#color body color230 color234 "<[Bb][Gg]>" # -#color body color136 color234 " [;:]-*[})>{(<|]" # :-) etc... -# *bold* -#color body color33 color234 "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" -#mono body bold "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" -# _underline_ -#color body color33 color234 "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" -#mono body underline "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" -# /italic/ (Sometimes gets directory names) -#color body color33 color234 "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" -#mono body underline "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" - -# Border lines. -#color body color33 color234 "( *[-+=#*~_]){6,}" - -#folder-hook . "color status J_black J_status " -#folder-hook gmail/inbox "color status J_black color136 " -#folder-hook gmail/important "color status J_black color136 " - diff --git a/mutt/compose.muttrc b/mutt/compose.muttrc deleted file mode 100644 index 6b8e8517..00000000 --- a/mutt/compose.muttrc +++ /dev/null @@ -1,5 +0,0 @@ -set forward_format = "Fwd: %s" -set fast_reply=yes -set forward_decode # decode when forwarding -set reply_to # reply to Reply to: field -set use_envelope_from = yes diff --git a/mutt/encypt.muttrc b/mutt/encypt.muttrc deleted file mode 100644 index 1eedbea3..00000000 --- a/mutt/encypt.muttrc +++ /dev/null @@ -1,23 +0,0 @@ - -set pgp_decode_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" -set pgp_verify_command="gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f" -set pgp_decrypt_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" -set pgp_sign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --detach-sign --textmode %?a?-u %a? %f" -set pgp_clearsign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --textmode --clearsign %?a?-u %a? %f" -set pgp_encrypt_only_command="pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f" -set pgp_encrypt_sign_command="pgpewrap gpg %?p?--passphrase-fd 0? --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f" - -set pgp_import_command="gpg --no-verbose --import %f" -set pgp_export_command="gpg --no-verbose --export --armor %r" -set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r" -set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --list-keys %r" -set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --list-secret-keys %r" - -set pgp_good_sign="^gpg: Good signature from" - -set pgp_use_gpg_agent = yes -set pgp_timeout = 3600 -set crypt_replyencrypt = yes -set crypt_replysign = yes - -set pgp_sign_as = 6EB517B0 diff --git a/mutt/headers.muttrc b/mutt/headers.muttrc deleted file mode 100644 index 62290913..00000000 --- a/mutt/headers.muttrc +++ /dev/null @@ -1,17 +0,0 @@ -# Ignore all headers -ignore * - -# Then un-ignore the ones I want to see -unignore From: -unignore To: -unignore Reply-To: -unignore Subject: -unignore Date: -unignore Organization: -unignore Newsgroups: -unignore CC: -unignore BCC: -unignore User-Agent: - -# Now order the visable header lines -hdr_order Date: From: To: CC: BCC: Reply-To: Subject: Organization: User-Agent: diff --git a/mutt/keymap.muttrc b/mutt/keymap.muttrc deleted file mode 100644 index 314de486..00000000 --- a/mutt/keymap.muttrc +++ /dev/null @@ -1,20 +0,0 @@ -macro index,pager q "~/.maildb/jgi/INBOX" "Go to jgi mail" -macro index,pager w '~/.maildb/jgi/[Gmail].All Mail' "Go to all mail" -macro index,pager e '~/.maildb/jgi/[Gmail].Sent Mail' "Go to sent mail" -macro index,pager r '~/.maildb/jgi/[Gmail].Drafts' "Go to drafts" - -macro index,pager a "~/.maildb/michaelbarton/INBOX" "Go to michaelbarton mail" -macro index,pager s '~/.maildb/michaelbarton/[Google Mail].All Mail' "Go to all mail" -macro index,pager d '~/.maildb/michaelbarton/[Google Mail].Sent Mail' "Go to sent mail" -macro index,pager f '~/.maildb/michaelbarton/[Google Mail].Drafts' "Go to drafts" - -# Search -macro index S "unset wait_keymutt-notmuch-py ~/.maildb/jgi/temporary/search+temporary/search" "search mail (using notmuch)" - -# Sidebar -macro index,pager "" "Next sidebar folder" -macro index,pager "" "Previous sidebar folder" -macro index,pager "" "Open selected sidebar folder" - -# Show encryption menu after composing message -bind compose p pgp-menu diff --git a/mutt/mutt-notmuch-py b/mutt/mutt-notmuch-py deleted file mode 100755 index 637b9e2e..00000000 --- a/mutt/mutt-notmuch-py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python -""" -mutt-notmuch-py - -This is a Gmail-only version of the original mutt-notmuch script. - -It will interactively ask you for a search query and then symlink the matching -messages to $HOME/.cache/mutt_results. - -Add this to your muttrc. - -macro index / "unset wait_keymutt-notmuch-py~/.cache/mutt_results" \ - "search mail (using notmuch)" - -This script overrides the $HOME/.cache/mutt_results each time you run a query. - -Install this by adding this file somewhere on your PATH. - -Tested on OSX Lion and Arch Linux. - -(c) 2012 - Honza Pokorny -Licensed under BSD -""" - -import os -import hashlib - -from commands import getoutput -from mailbox import Maildir -from optparse import OptionParser -from collections import defaultdict - - -def digest(filename): - with open(filename) as f: - return hashlib.sha1(f.read()).hexdigest() - - -def pick_all_mail(messages): - for m in messages: - if "All Mail" in m: - return m - - -def empty_dir(directory): - box = Maildir(directory) - box.clear() - - -def command(cmd): - return getoutput(cmd) - - -def main(dest_box, is_gmail): - query = raw_input("Query: ") - - command("mkdir -p %s/cur" % dest_box) - command("mkdir -p %s/new" % dest_box) - - empty_dir(dest_box) - - files = command("notmuch search --output=files %s" % query).split("\n") - - data = defaultdict(list) - messages = [] - - for f in files: - if not f: - continue - - try: - sha = digest(f) - data[sha].append(f) - except IOError: - print("File %s does not exist" % f) - - for sha in data: - if is_gmail and len(data[sha]) > 1: - messages.append(pick_all_mail(data[sha])) - else: - messages.append(data[sha][0]) - - for m in messages: - if not m: - continue - - target = os.path.join(dest_box, "cur", os.path.basename(m)) - if not os.path.exists(target): - os.symlink(m, target) - - -if __name__ == "__main__": - p = OptionParser("usage: %prog [OPTIONS] [RESULTDIR]") - p.add_option( - "-g", - "--gmail", - dest="gmail", - action="store_true", - default=True, - help="gmail-specific behavior", - ) - p.add_option( - "-G", "--not-gmail", dest="gmail", action="store_false", help="gmail-specific behavior" - ) - (options, args) = p.parse_args() - - if args: - dest = args[0] - else: - dest = "~/.cache/mutt_results" - - # Use expanduser() so that os.symlink() won't get weirded out by tildes. - main(os.path.expanduser(dest).rstrip("/"), options.gmail) diff --git a/mutt/muttrc b/mutt/muttrc deleted file mode 100644 index e3f8d978..00000000 --- a/mutt/muttrc +++ /dev/null @@ -1,29 +0,0 @@ -# General options - -set realname = "Michael Barton" -set mbox_type = Maildir - -my_hdr X-URL: http://www.michaelbarton.me.uk - -set editor = "nvim" -set mail_check = 60 -set timeout = 60 -set header_cache=~/.mutt/cache/headers -set message_cachedir=~/.mutt/cache/bodies -set move = no -set include -set auto_tag = yes -alternative_order text/plain text/html * -auto_view text/html -bind editor ^T complete -set query_command="/usr/local/bin/lbdbq '%s'" -set pager_index_lines= 10 -set implicit_autoview = yes - -source "$HOME/.dotfiles/mutt/compose.muttrc" -source "$HOME/.dotfiles/mutt/appearance.muttrc" -source "$HOME/.dotfiles/mutt/headers.muttrc" -source "$HOME/.dotfiles/mutt/keymap.muttrc" -source "$HOME/.dotfiles/mutt/colors.muttrc" -source "$HOME/.dotfiles/mutt/encypt.muttrc" -source "$HOME/.dotfiles/mutt/accounts.muttrc" diff --git a/notmuch-config b/notmuch-config deleted file mode 100644 index e4150838..00000000 --- a/notmuch-config +++ /dev/null @@ -1,88 +0,0 @@ -# .notmuch-config - Configuration file for the notmuch mail system -# -# For more information about notmuch, see http://notmuchmail.org - -# Database configuration -# -# The only value supported here is 'path' which should be the top-level -# directory where your mail currently exists and to where mail will be -# delivered in the future. Files should be individual email messages. -# Notmuch will store its database within a sub-directory of the path -# configured here named ".notmuch". -# -[database] -path=/Users/mbarton/.maildb/jgi - -# User configuration -# -# Here is where you can let notmuch know how you would like to be -# addressed. Valid settings are -# -# name Your full name. -# primary_email Your primary email address. -# other_email A list (separated by ';') of other email addresses -# at which you receive email. -# -# Notmuch will use the various email addresses configured here when -# formatting replies. It will avoid including your own addresses in the -# recipient list of replies, and will set the From address based on the -# address to which the original email was addressed. -# -[user] -name=Michael Barton -primary_email=mbarton@lbl.gov -other_email=mail@michaelbarton.me.uk; - -# Configuration for "notmuch new" -# -# The following options are supported here: -# -# tags A list (separated by ';') of the tags that will be -# added to all messages incorporated by "notmuch new". -# -# ignore A list (separated by ';') of file and directory names -# that will not be searched for messages by "notmuch new". -# -# NOTE: *Every* file/directory that goes by one of those -# names will be ignored, independent of its depth/location -# in the mail store. -# -[new] -tags=unread;inbox; -ignore= - -# Search configuration -# -# The following option is supported here: -# -# exclude_tags -# A ;-separated list of tags that will be excluded from -# search results by default. Using an excluded tag in a -# query will override that exclusion. -# -[search] -exclude_tags=deleted;spam; - -# Maildir compatibility configuration -# -# The following option is supported here: -# -# synchronize_flags Valid values are true and false. -# -# If true, then the following maildir flags (in message filenames) -# will be synchronized with the corresponding notmuch tags: -# -# Flag Tag -# ---- ------- -# D draft -# F flagged -# P passed -# R replied -# S unread (added when 'S' flag is not present) -# -# The "notmuch new" command will notice flag changes in filenames -# and update tags, while the "notmuch tag" and "notmuch restore" -# commands will notice tag changes and update flags in filenames -# -[maildir] -synchronize_flags=true diff --git a/nvim/lua/config/dbt.lua b/nvim/lua/config/dbt.lua index 962891bd..dd226b57 100644 --- a/nvim/lua/config/dbt.lua +++ b/nvim/lua/config/dbt.lua @@ -82,7 +82,7 @@ vim.keymap.set("n", "dg", function() -- Try to find ref('model') or ref("model") around cursor local ref_model = nil - for start_pos, name, end_pos in line:gmatch("()ref%(['\"]([^'\"]+)['\"]%)()" ) do + for start_pos, name, end_pos in line:gmatch("()ref%(['\"]([^'\"]+)['\"]%)()") do if col >= start_pos and col <= end_pos then ref_model = name break @@ -92,7 +92,7 @@ vim.keymap.set("n", "dg", function() -- Try source('source_name', 'table_name') if no ref found local source_name, source_table = nil, nil if not ref_model then - for start_pos, src, tbl, end_pos in line:gmatch("()source%(['\"]([^'\"]+)['\"]%s*,%s*['\"]([^'\"]+)['\"]%)()" ) do + for start_pos, src, tbl, end_pos in line:gmatch("()source%(['\"]([^'\"]+)['\"]%s*,%s*['\"]([^'\"]+)['\"]%)()") do if col >= start_pos and col <= end_pos then source_name, source_table = src, tbl break @@ -157,17 +157,23 @@ vim.keymap.set("n", "df", function() map("i", "", function() local name = get_model_name() actions.close(prompt_bufnr) - if name then dbt_cmd_raw("uv run dbt run -s " .. name) end + if name then + dbt_cmd_raw("uv run dbt run -s " .. name) + end end) map("i", "", function() local name = get_model_name() actions.close(prompt_bufnr) - if name then dbt_cmd_raw("uv run dbt build -s " .. name) end + if name then + dbt_cmd_raw("uv run dbt build -s " .. name) + end end) map("i", "", function() local name = get_model_name() actions.close(prompt_bufnr) - if name then dbt_cmd_raw("uv run dbt test -s " .. name) end + if name then + dbt_cmd_raw("uv run dbt test -s " .. name) + end end) return true end, @@ -317,7 +323,13 @@ for line in raw.splitlines(): w.writerows(preview) break "]] - local cmd = "cd " .. root .. " && uv run dbt show -s " .. model .. " --limit 500 --output json --log-format json | " .. json_to_csv .. " | vd -f csv" + local cmd = "cd " + .. root + .. " && uv run dbt show -s " + .. model + .. " --limit 500 --output json --log-format json | " + .. json_to_csv + .. " | vd -f csv" require("toggleterm.terminal").Terminal :new({ cmd = cmd, @@ -402,7 +414,12 @@ vim.keymap.set("n", "dA", function() local script_path = vim.fn.stdpath("config") .. "/dbt/dbt_analyse.py" local shell_script = string.format( [[cd %s && uv run python3 %s --model %s --root %s --filepath %s --prompt %s || (echo "Press enter to close..." && read)]], - root, script_path, model, root, filepath, prompt_path + root, + script_path, + model, + root, + filepath, + prompt_path ) vim.fn.jobstart({ "tmux", "new-window", "-n", "dbt:" .. model, shell_script }, { detach = true }) vim.notify("Opened interactive cursor-agent session in tmux window 'dbt:" .. model .. "'", vim.log.levels.INFO) diff --git a/nvim/lua/config/providers.lua b/nvim/lua/config/providers.lua index d5f7de76..142581d9 100644 --- a/nvim/lua/config/providers.lua +++ b/nvim/lua/config/providers.lua @@ -17,4 +17,4 @@ vim.g.loaded_ruby_provider = 0 -- Python provider is useful for some plugins -- To enable, run: pip3 install neovim -- For now, we'll leave it enabled as it's commonly used --- vim.g.loaded_python3_provider = 0 \ No newline at end of file +-- vim.g.loaded_python3_provider = 0 diff --git a/nvim/lua/plugins/fyler.lua b/nvim/lua/plugins/fyler.lua index ea766cd1..a87c39f6 100644 --- a/nvim/lua/plugins/fyler.lua +++ b/nvim/lua/plugins/fyler.lua @@ -63,4 +63,3 @@ return { }, }, } - diff --git a/nvim/lua/plugins/language.lua b/nvim/lua/plugins/language.lua index b064ff34..e33bf08f 100644 --- a/nvim/lua/plugins/language.lua +++ b/nvim/lua/plugins/language.lua @@ -54,8 +54,9 @@ return { formatters = { mdformat = { prepend_args = { - "--wrap", "80", -- Wrap text at 80 characters - "--number", -- Use numbered lists consistently + "--wrap", + "80", -- Wrap text at 80 characters + "--number", -- Use numbered lists consistently }, }, prettier = { diff --git a/nvim/lua/plugins/toggleterm.lua b/nvim/lua/plugins/toggleterm.lua index 1c4e33fe..e1cfc46c 100644 --- a/nvim/lua/plugins/toggleterm.lua +++ b/nvim/lua/plugins/toggleterm.lua @@ -3,6 +3,8 @@ return { version = "*", config = function() local toggleterm = require("toggleterm") + local Terminal = require("toggleterm.terminal").Terminal + local terminal_module = require("toggleterm.terminal") toggleterm.setup({ -- Terminal window size @@ -30,58 +32,108 @@ return { shell = vim.o.shell, -- use your default shell }) - -- Set up key mappings for terminals - vim.keymap.set("n", "tt", "ToggleTerm", { desc = "[T]oggle [T]erminal" }) - vim.keymap.set("n", "tf", "ToggleTerm direction=float", { desc = "[T]oggle [F]loating terminal" }) - vim.keymap.set( - "n", - "tv", - "ToggleTerm direction=vertical", - { desc = "[T]oggle [V]ertical terminal" } - ) - vim.keymap.set( - "n", - "th", - "ToggleTerm direction=horizontal", - { desc = "[T]oggle [H]orizontal terminal" } - ) - - -- Send text to terminal - vim.keymap.set("n", "tl", function() - local term_id = vim.v.count > 0 and vim.v.count or 1 + local role_config = { + run = { count = 1, direction = "horizontal" }, + test = { count = 2, direction = "horizontal" }, + scratch = { count = 3, direction = "float" }, + sql = { count = 4, direction = "horizontal", cmd = "duckdb" }, + } + + local role_order = { "run", "test", "scratch", "sql" } + local managed_terminals = {} + local last_role = "run" - -- Get terminal by ID and toggle it if closed - local term = require("toggleterm.terminal").get(term_id) - if term and not term:is_open() then - term:toggle() + local function notify_role(action, role) + vim.notify(string.format("%s %s terminal", action, role), vim.log.levels.INFO, { title = "ToggleTerm" }) + end + + local function ensure_terminal(role) + local cfg = role_config[role] + if not managed_terminals[role] then + managed_terminals[role] = Terminal:new({ + count = cfg.count, + direction = cfg.direction, + cmd = cfg.cmd, + hidden = true, + close_on_exit = false, + }) end + return managed_terminals[role] + end - -- Send the current line - require("toggleterm").send_lines_to_terminal("single_line", true, { args = term_id }) - end, { desc = "[T]erminal send [L]ine" }) + local function open_role(role) + local term = ensure_terminal(role) + term:open() + last_role = role + notify_role("Focused", role) + return term + end - vim.keymap.set("v", "ts", function() - local term_id = vim.v.count > 0 and vim.v.count or 1 + local function toggle_last_role() + local term = ensure_terminal(last_role) + term:toggle() + notify_role("Toggled", last_role) + end - -- Get terminal by ID and toggle it if closed - local term = require("toggleterm.terminal").get(term_id) - if term and not term:is_open() then - term:toggle() + local function close_current_terminal() + local current_id = vim.b.toggle_number + if current_id then + local term = terminal_module.get(current_id) + if term then + term:close() + vim.notify("Closed current terminal", vim.log.levels.INFO, { title = "ToggleTerm" }) + return + end end - -- Send the visual selection - require("toggleterm").send_lines_to_terminal("visual_selection", true, { args = term_id }) - end, { desc = "[T]erminal [S]end visual selection" }) + local term = ensure_terminal(last_role) + if term:is_open() then + term:close() + notify_role("Closed", last_role) + else + vim.notify("No managed terminal is currently open", vim.log.levels.INFO, { title = "ToggleTerm" }) + end + end + + local function close_all_terminals() + local closed = 0 + for _, role in ipairs(role_order) do + local term = managed_terminals[role] + if term and term:is_open() then + term:close() + closed = closed + 1 + end + end + + if closed == 0 then + vim.notify("No managed terminals were open", vim.log.levels.INFO, { title = "ToggleTerm" }) + else + vim.notify( + string.format("Closed %d managed terminal(s)", closed), + vim.log.levels.INFO, + { title = "ToggleTerm" } + ) + end + end - -- Run a just target via a picker - vim.keymap.set("n", "tj", function() + local function send_to_last_target(mode) + local term = open_role(last_role) + local send_mode = mode == "visual" and "visual_selection" or "single_line" + require("toggleterm").send_lines_to_terminal(send_mode, true, { args = term.id }) + if mode == "visual" then + notify_role("Sent selection to", last_role) + else + notify_role("Sent line to", last_role) + end + end + + local function run_just_target() local output = vim.fn.systemlist("just --list --unsorted 2>/dev/null") if vim.v.shell_error ~= 0 or #output == 0 then vim.notify("No justfile found or no targets available", vim.log.levels.WARN) return end - -- Skip the header line ("Available recipes:") and parse target names local targets = {} for i, line in ipairs(output) do if i > 1 then @@ -93,37 +145,79 @@ return { end vim.ui.select(targets, { prompt = "just target> " }, function(choice) - if choice then - require("toggleterm.terminal").Terminal - :new({ cmd = "just " .. choice, close_on_exit = false, direction = "horizontal" }) - :toggle() + if not choice then + return + end + + local term = open_role("run") + if term.job_id then + vim.api.nvim_chan_send(term.job_id, "just " .. choice .. "\n") + vim.notify("Started just " .. choice .. " in run terminal", vim.log.levels.INFO, { title = "ToggleTerm" }) + else + vim.notify("Run terminal is not ready yet", vim.log.levels.WARN, { title = "ToggleTerm" }) end end) - end, { desc = "[T]oggle [J]ust target" }) - - -- Terminal navigation keymaps - function _G.set_terminal_keymaps() - local opts = { buffer = 0 } - vim.keymap.set("t", "", [[]], opts) - vim.keymap.set("t", "jk", [[]], opts) - vim.keymap.set("t", "", [[TmuxNavigateLeft]], opts) - vim.keymap.set("t", "", [[TmuxNavigateDown]], opts) - vim.keymap.set("t", "", [[TmuxNavigateUp]], opts) - vim.keymap.set("t", "", [[TmuxNavigateRight]], opts) - vim.keymap.set("t", "", [[]], opts) end - -- Auto-apply terminal keymaps when opening a terminal - vim.cmd("autocmd! TermOpen term://* lua set_terminal_keymaps()") + local term_keymaps_group = vim.api.nvim_create_augroup("ToggleTermLocalKeymaps", { clear = true }) + vim.api.nvim_create_autocmd("TermOpen", { + group = term_keymaps_group, + pattern = "term://*", + callback = function(event) + if vim.bo[event.buf].filetype ~= "toggleterm" then + return + end + + local opts = { buffer = event.buf } + vim.keymap.set("t", "", [[]], opts) + vim.keymap.set("t", "jk", [[]], opts) + -- Route directional moves through vim-tmux-navigator for consistent pane navigation. + vim.keymap.set("t", "", [[TmuxNavigateLeft]], opts) + vim.keymap.set("t", "", [[TmuxNavigateDown]], opts) + vim.keymap.set("t", "", [[TmuxNavigateUp]], opts) + vim.keymap.set("t", "", [[TmuxNavigateRight]], opts) + vim.keymap.set("t", "", [[]], opts) + end, + }) + + vim.keymap.set("n", "tr", function() + open_role("run") + end, { desc = "[T]erminal [R]un" }) + + vim.keymap.set("n", "te", function() + open_role("test") + end, { desc = "[T]erminal t[E]st" }) + + vim.keymap.set("n", "ts", function() + open_role("scratch") + end, { desc = "[T]erminal [S]cratch" }) + + vim.keymap.set("n", "tq", function() + open_role("sql") + end, { desc = "[T]erminal [Q]uery (duckdb)" }) + + vim.keymap.set("n", "ta", toggle_last_role, { desc = "[T]erminal toggle l[A]st" }) + vim.keymap.set("n", "tx", close_current_terminal, { desc = "[T]erminal close current" }) + vim.keymap.set("n", "tX", close_all_terminals, { desc = "[T]erminal close all" }) + vim.keymap.set("n", "tl", function() + send_to_last_target("line") + end, { desc = "[T]erminal send [L]ine" }) + vim.keymap.set("v", "tl", function() + send_to_last_target("visual") + end, { desc = "[T]erminal send [L]ines" }) + vim.keymap.set("n", "tj", run_just_target, { desc = "[T]erminal run [J]ust target" }) end, keys = { { "", desc = "Toggle terminal" }, - { "tt", desc = "Toggle terminal" }, - { "tf", desc = "Toggle floating terminal" }, - { "tv", desc = "Toggle vertical terminal" }, - { "th", desc = "Toggle horizontal terminal" }, - { "ts", desc = "Send visual selection to terminal", mode = "v" }, + { "tr", desc = "Open run terminal" }, + { "te", desc = "Open test terminal" }, + { "ts", desc = "Open scratch terminal" }, + { "tq", desc = "Open duckdb query terminal" }, + { "ta", desc = "Toggle last terminal" }, + { "tx", desc = "Close current terminal" }, + { "tX", desc = "Close all terminals" }, { "tl", desc = "Send line to terminal" }, - { "tj", desc = "Run just target" }, + { "tl", desc = "Send selection to terminal", mode = "v" }, + { "tj", desc = "Run just target in terminal" }, }, } diff --git a/nvim/lua/plugins/vimwiki.lua b/nvim/lua/plugins/vimwiki.lua index adee2cd2..40f053f4 100644 --- a/nvim/lua/plugins/vimwiki.lua +++ b/nvim/lua/plugins/vimwiki.lua @@ -16,7 +16,7 @@ return { pattern = "vimwiki", group = group, callback = function() - pcall(vim.keymap.del, 'n', 'wn') + pcall(vim.keymap.del, "n", "wn") end, }) end, diff --git a/offlineimap/offlineimap.py b/offlineimap/offlineimap.py deleted file mode 100644 index d46fd1cb..00000000 --- a/offlineimap/offlineimap.py +++ /dev/null @@ -1,21 +0,0 @@ -import subprocess - - -def get_keychain_pass(account): - command = ["/usr/bin/security", "find-generic-password", "-w", "-a", account] - process = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - process.wait() - if process.returncode != 0: - raise RuntimeError("Error running command: {}".format(process.stderr.read().decode())) - - password_block = process.stdout.read().decode().strip() - if password_block: - return password_block - raise RuntimeError( - "No password found with `{}`:\n {}".format(" ".join(command), password_block) - ) - - -def get_keyring_pass(key, value): - command = ["secret-tool", "lookup", key, value] - return subprocess.check_output(command).decode().strip() diff --git a/offlineimap/rc.linux b/offlineimap/rc.linux deleted file mode 100644 index 10a6646d..00000000 --- a/offlineimap/rc.linux +++ /dev/null @@ -1,25 +0,0 @@ -# Applies to all accounts -[general] -metadata = ~/.offlineimap -pythonfile = ~/.offlineimap/offlineimap.py -maxsyncaccounts = 2 -ui = basic -ssl = yes -accounts = Personal - -[Account Personal] -localrepository = personal-local -remoterepository = personal-remote -maxage = 30 - -[Repository personal-local] -type = Maildir -localfolders = ~/.maildb/ -restoreatime = no - -[Repository personal-remote] -type = Gmail -remoteuser = mail@michaelbarton.me.uk -remotepasseval = get_keyring_pass("password", "gmail") -realdelete = no -sslcacertfile = /etc/ssl/certs/ca-certificates.crt diff --git a/offlineimap/rc.osx b/offlineimap/rc.osx deleted file mode 100644 index af01c943..00000000 --- a/offlineimap/rc.osx +++ /dev/null @@ -1,26 +0,0 @@ -# Applies to all accounts -[general] -metadata = ~/.offlineimap -pythonfile = ~/.offlineimap.py -maxsyncaccounts = 2 -ui = basic -ssl = yes -accounts = Personal - -[Account Personal] -localrepository = personal-local -remoterepository = personal-remote -maxage = 30 - -[Repository personal-local] -type = Maildir -localfolders = ~/.maildb/michaelbarton -restoreatime = no - -[Repository personal-remote] -type = Gmail -remoteuser = mail@michaelbarton.me.uk -remotepasseval = get_keychain_pass(account="acct.gmail") -realdelete = no -# This is installed by `brew install openssl` and may change with versions -sslcacertfile = /usr/local/etc/openssl@3/cert.pem diff --git a/offlineimap/restart_offlineimap b/offlineimap/restart_offlineimap deleted file mode 100755 index 4d31edd6..00000000 --- a/offlineimap/restart_offlineimap +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -launchctl stop uk.me.michaelbarton.offlineimap -launchctl start uk.me.michaelbarton.offlineimap diff --git a/offlineimap/uk.me.michaelbarton.offlineimap.plist b/offlineimap/uk.me.michaelbarton.offlineimap.plist deleted file mode 100644 index 6218aa04..00000000 --- a/offlineimap/uk.me.michaelbarton.offlineimap.plist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Debug - - KeepAlive - - Label - uk.me.michaelbarton.offlineimap - ProgramArguments - - /usr/local/bin/offlineimap - -q - -u - Noninteractive.Quiet - - RunAtLoad - - StartInterval - 240 - - diff --git a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/Justfile b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/Justfile index feb83df0..8f22c3e3 100644 --- a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/Justfile +++ b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/Justfile @@ -1,17 +1,16 @@ # Launch Jupyter Notebook notebook: - poetry run jupyter notebook notebooks/ + uv run jupyter notebook notebooks/ -# Format the code using black +# Format and lint the code using ruff fmt: - poetry run isort . - poetry run black . + uv run ruff format . + uv run ruff check --fix . # Run the tests using pytest test: - poetry run pytest tests/ + uv run pytest tests/ # Run the main script run: - poetry run python {{ cookiecutter.package_name }}/main.py - + uv run python {{ cookiecutter.package_name }}/main.py diff --git a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/pyproject.toml b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/pyproject.toml index 79f82d8c..6e761062 100644 --- a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/pyproject.toml +++ b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/pyproject.toml @@ -1,25 +1,26 @@ -[tool.poetry] -name = "karius_taxonomy_mapping" +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[project] +name = "{{ cookiecutter.package_name }}" version = "0.1.0" description = "" -authors = ["Your Name "] - -[tool.poetry.dependencies] -python = "^3.10" -jupyter = "^1.0.0" +requires-python = ">=3.10" +dependencies = [ + "jupyter>=1.0.0", +] -[tool.poetry.dev-dependencies] -bump2version = "^1.0.1" -edx-lint = "^5.3.6" -mypy = "^1.8.0" -pandas-stubs = "^2.2.0.240218" -pydocstyle = "^6.3.0" -pytest = "^9.0.3" -ruff = "^0.8.0" +[dependency-groups] +dev = [ + "mypy>=1.8.0", + "pandas-stubs>=2.2.0.240218", + "pytest>=9.0.3", + "ruff>=0.8.0", +] -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.setuptools.packages.find] +include = ["{{ cookiecutter.package_name }}*"] [tool.ruff] line-length = 100 @@ -37,8 +38,15 @@ select = [ "RUF", # ruff-specific rules ] -[[tool.poetry.source]] -name = 'jfrog' -url = 'https://karius.jfrog.io/artifactory/api/pypi/karius-pypi-virtual/simple/' -secondary = true +# Private packages: CodeArtifact index `karius`. `aws@` in the URL is required +# for CodeArtifact basic auth + keyrings.codeartifact. User-level: +# `keyring-provider = "subprocess"` in ~/.config/uv/uv.toml, `uv tool install +# keyring --with keyrings.codeartifact`. See: +# https://docs.astral.sh/uv/guides/integration/aws/ +[[tool.uv.index]] +name = "karius" +url = "https://aws@karius-573640641260.d.codeartifact.us-east-1.amazonaws.com/pypi/karius-pypi/simple/" +explicit = true +# [tool.uv.sources] +# kli = { index = "karius" } diff --git a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/tests/test_main.py b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/tests/test_main.py index e69de29b..3ada1ee4 100644 --- a/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/tests/test_main.py +++ b/python/cookiecutter/scratch-python-template/{{cookiecutter.timestamp}}__{{cookiecutter.project_name}}/tests/test_main.py @@ -0,0 +1,2 @@ +def test_placeholder(): + assert True diff --git a/ripgrep/rc b/ripgrep/rc index 40117e54..473c6778 100644 --- a/ripgrep/rc +++ b/ripgrep/rc @@ -1,6 +1,6 @@ # ripgrep config file # Set RIPGREP_CONFIG_PATH=$HOME/.config/ripgrep/rc to activate -# (set in fish/config.fish) +# (set in shell configs in this repo) # Smart case: case-insensitive unless pattern has uppercase --smart-case diff --git a/systemd/user/offlineimap.service b/systemd/user/offlineimap.service deleted file mode 100644 index 2ca4ce17..00000000 --- a/systemd/user/offlineimap.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Sync email to computuer - -[Service] -# execute the action and stop -Type=oneshot -ExecStart=/usr/bin/offlineimap - -# give 120 seconds for offlineimap to gracefully stop before hard killing it -TimeoutStopSec=120 - -[Install] -WantedBy=mail.target diff --git a/systemd/user/offlineimap.timer b/systemd/user/offlineimap.timer deleted file mode 100644 index 36310d07..00000000 --- a/systemd/user/offlineimap.timer +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Offlineimap Query Timer -Requires=offlineimap.service - -[Timer] -# start the service 2 minutes after a boot-up -OnBootSec=2m - -# the timer will run the service every 5 minutes -OnUnitInactiveSec=5m - -[Install] -WantedBy=default.target diff --git a/tmux/cheatsheet.md b/tmux/cheatsheet.md index 21ab00bc..eae71ca9 100644 --- a/tmux/cheatsheet.md +++ b/tmux/cheatsheet.md @@ -48,6 +48,28 @@ are unbound to avoid conflicts. | `y` | Yank selection | | `q` | Exit copy mode | +## Neovim ToggleTerm (`t`) + +Fixed terminal roles make it easier to keep context: + +| Keys | Action | +| ------------ | -------------------------------------------------------- | +| `tr` | Focus run terminal (slot 1) | +| `te` | Focus test terminal (slot 2) | +| `ts` | Focus scratch terminal (slot 3) | +| `ta` | Toggle last-focused terminal | +| `tx` | Close current terminal | +| `tX` | Close all managed terminals | +| `tl` | Send line (or visual selection) to last-focused terminal | +| `tj` | Pick and run a `just` target in the run terminal | + +Inside ToggleTerm buffers: + +| Keys | Action | +| -------------- | ------------------------------------- | +| `` / `jk` | Leave terminal insert mode | +| `Ctrl-h/j/k/l` | Navigate panes via vim-tmux-navigator | + ## Ghostty | Keys | Action | diff --git a/urlview b/urlview deleted file mode 100644 index 2445afd0..00000000 --- a/urlview +++ /dev/null @@ -1 +0,0 @@ -COMMAND open %s diff --git a/uv/uv.toml b/uv/uv.toml new file mode 100644 index 00000000..cab1b162 --- /dev/null +++ b/uv/uv.toml @@ -0,0 +1 @@ +keyring-provider = "subprocess" diff --git a/visidata/visidatarc b/visidata/visidatarc new file mode 100644 index 00000000..959676e6 --- /dev/null +++ b/visidata/visidatarc @@ -0,0 +1,2 @@ +options.motd_url = '' +options.disp_date_fmt = '%Y-%m-%d' diff --git a/yml/format_yml.py b/yml/format_yml.py deleted file mode 100755 index a1e4b49c..00000000 --- a/yml/format_yml.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import yaml - -with open(sys.argv[1]) as fh_in: - contents = yaml.safe_load(fh_in) - -with open(sys.argv[1], "w") as fh_out: - fh_out.write(yaml.dump(contents))