From 742e5ab8476710cba3656659d47dc8487a5f0b82 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Tue, 24 Mar 2026 01:29:10 -0300 Subject: [PATCH 01/23] docs: add doc folder and agents.md --- AGENTS.md | 26 ++++++++++ doc/bin.md | 47 +++++++++++++++++ doc/dotfiles.md | 59 +++++++++++++++++++++ doc/etc.md | 90 ++++++++++++++++++++++++++++++++ doc/structure.md | 38 ++++++++++++++ doc/workflows.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 390 insertions(+) create mode 100644 AGENTS.md create mode 100644 doc/bin.md create mode 100644 doc/dotfiles.md create mode 100644 doc/etc.md create mode 100644 doc/structure.md create mode 100644 doc/workflows.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fc7b3d9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,26 @@ +# Agent guidelines for interface -- a personal dotfiles and script repo + +## AI Role, behavior, system prompt + +We are senior software engineers hacking some shell script together. +I'm reviewing your code and explaining how the codebase is designed. +I'll also give you tasks, directions, we'll be working together so let's have a good time :) +What matters is good design, clean code and reducing maintenance, performance comes second. +See files under doc/ for project structure and documentation (faster than reading the source code) + +## Build and Test Commands + +Most of the scripts have no tests, we gotta run them to test. + +## Code Style + +Keep comments short and sweet, don't document obvious code. +**Formatting:** We use `shfmt`. + +## Misc + +be more minimalistic: being helpful is good but we need to right answer, avoid guessing or crazy workarounds. +avoid single letter vars if their scope is not small. +when we refactor, minimize renames unless asked for. +run formatter as last step after making code changes. +this is a jujutsu repo and do not make commits. diff --git a/doc/bin.md b/doc/bin.md new file mode 100644 index 0000000..9141730 --- /dev/null +++ b/doc/bin.md @@ -0,0 +1,47 @@ +# bin/ — Executables + +All scripts require `$BASH_ENV` to be set (sources `lib.sh` for `msgln`, `log`, `err`, `fatal`, etc.). +Scripts use the standard header: `set -euo pipefail`, `trap 'err $LINENO' ERR`. + +## Shell Scripts + +| Script | Purpose | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `snapshot` | Interactive btrfs subvolume snapshot tool. Prompts for source subvolume under `/toplevel`, name, and read-only flag. Supports `--dry-run`. | +| `system-snapshot` | Non-interactive btrfs snapshot for automated use. Takes target subvolume names as args, keeps max 50 snapshots, trims old ones. | +| `system-up` | Full Arch Linux system update: snapshot → stop postgres → update pacman/AUR/mise globals → migrate postgres if major version changed → btrfs scrub optional → kexec into new kernel. | +| `btrfs-scrub` | Runs `btrfs scrub` on specified disks. | +| `app-inhibit-sleep` | Watches if Firefox (or given apps) is playing audio via `playerctl`; calls `systemd-inhibit` to prevent idle sleep while playing. | +| `lazy-git` | Interactive git helper: stages, composits commitlint-compliant commit messages, and pushes repos listed in `$PUSH_REPOS`. Reads `$GIT_BRANCH`. | +| `lazy-jujutsu` | Same as `lazy-git` but for jujutsu (`jj`). | +| `extract` | Universal archive extractor. Detects format and calls the right tool (tar, unzip, 7z, etc.). | +| `hue` | Philips Hue light controller (~425 lines). Talks to the Hue bridge REST API. | +| `compile-bin` | Builds all Go tools under `src/` into `bin/`. Iterates `src/**/main.go`, runs `go build -race`. | +| `bulk-curl-from-file` | Runs curl for each URL in a file. | +| `cpfgen` | CPF number generator (Brazilian tax ID). | +| `dictation-keyboard-hook` | Keyboard hook for dictation shortcut. | +| `dir-rename` | Compiled Go binary — bulk directory rename tool. | +| `download-youtube-id` | Downloads a YouTube video by ID using `yt-dlp`. | +| `forex` | Compiled Go binary — currency exchange rate fetcher. | +| `game-audio-fix` | Fixes audio for games (likely resets PulseAudio/PipeWire). | +| `mac-defaults` | Applies `defaults write` settings for macOS. | +| `presentvalue` | Compiled Go binary — present value financial calculator. | +| `rename` | Perl script for powerful batch file renaming (regex-based). | +| `system-up` | (see above) | +| `template` | Scaffold script — creates new scripts from a template. | +| `typo` | Compiled binary — likely a typo/spell checker helper. | + +## Go Sources (`src/`) + +Built by `compile-bin`. Each subdirectory has a `main.go`. + +| Tool | Description | +| -------------- | ----------------------------------------- | +| `forex` | Fetches live exchange rates | +| `presentvalue` | Present value / financial math calculator | + +`dir-rename` and `typo` binaries are present in `bin/` but their source is not in this repo (compiled elsewhere or vendored as binaries). + +## git/ hooks + +`bin/git/interface-hook-pre-commit` — pre-commit hook for this repo (cspell spellcheck, shfmt formatting, etc.). diff --git a/doc/dotfiles.md b/doc/dotfiles.md new file mode 100644 index 0000000..ea5657b --- /dev/null +++ b/doc/dotfiles.md @@ -0,0 +1,59 @@ +# dotfiles/ + +Files here are symlinked into `$HOME`. The install mechanism is not in this repo +(managed manually or by a separate tool). + +## Shell + +| File | Purpose | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `.bashrc` | Main interactive shell config. Sets exports, sources libs and aliases. | +| `.bash_profile` | Login shell — sources `.bashrc`. | +| `.bash_env` | Non-interactive env loaded via `$BASH_ENV`. Sets `$GOPRIVATE`, `$T0_COLOR`, sources `lib.sh`, extends `$PATH`. | +| `.bash_logout` | Cleanup on logout. | +| `.bashrc.linux.sh` | Linux-specific bash config. | +| `.bashrc.mac.sh` | macOS-specific bash config. | +| `.aliases.sh` | Cross-platform aliases: navigation, work shortcuts (psql, gcloud), typo corrections, git aliases. | +| `.aliases.linux.sh` | Linux aliases: ls, mv, wl-copy/paste, pacman/yay wrappers, chromium VPN. | +| `.aliases.mac.sh` | macOS aliases. | +| `lib.sh` | **Core library.** Logging functions: `log`, `debug`, `err`, `warn`, `fatal`, `msg`, `msgln`. Helpers: `macos`, `requested_help`, `is_me`, `term_emulator`. Color via `$T0_COLOR`. Kept in sync with `github.com/tcodes0/sh`. | +| `lib-git-prompt.sh` | Git prompt helpers, exports `$GIT_BRANCH`. | +| `lib-prompt.sh` | PS1 prompt builder. | + +## Key exports (from `.bashrc`) + +| Variable | Value | +| ------------- | ----------------------------------------------------------- | +| `DOTFILES` | `$HOME/Desktop/interface/dotfiles` | +| `BASH_ENV` | `$HOME/.bash_env` | +| `GOPRIVATE` | `github.com/eleanorhealth/* github.com/tcodes0/*` | +| `T0_COLOR` | `true` (enables ANSI color in lib.sh logging) | +| `EDITOR` | `code -w` | +| `PUSH_REPOS` | Space-separated list of repo names for `lazy-git` auto-push | +| `KNOWN_HOSTS` | Array of known machine hostnames | + +## .config/ + +| Path | Purpose | +| ----------------------------------- | ------------------------------------------------------------------------- | +| `Claude/claude_desktop_config.json` | Claude Desktop config. Registers `jailMPC` MCP server via Docker Compose. | +| `jj/config.toml` | Jujutsu global config. | +| `jj/repos` | Known jj repos list. | +| `code*/User/` | VSCode / VSCodium settings and keybindings. | +| `systemd/user/` | User systemd service units. | +| `autostart/guake.desktop` | Autostart Guake terminal on login. | +| `keymap.xkb` | Custom XKB keyboard layout. | +| `htop/htoprc` | htop config. | +| `ollama/` | Ollama model config files (`aux-chat`, `aux-code`). | +| `fontconfig/` | Font rendering config. | + +## Other dotfiles + +| File | Purpose | +| -------------------------- | ------------------------------------------- | +| `.emacs` | Emacs config. | +| `.fonts/` | Custom fonts (DejaVu, 16bit, etc.). | +| `.commitlintrc.yml` | Commitlint rules for conventional commits. | +| `.cspell.config.yml` | cspell dictionary/config for spellchecking. | +| `.chatgpt-cli-config.yaml` | chatgpt-cli config. | +| `.fonts.conf` | Fontconfig (legacy location). | diff --git a/doc/etc.md b/doc/etc.md new file mode 100644 index 0000000..007ad7b --- /dev/null +++ b/doc/etc.md @@ -0,0 +1,90 @@ +# etc/ — System Configuration + +Files here are symlinked (or hardlinked for `fstab`) into `/etc`. Requires root for install. +Note: `/etc/fstab` must be a **hardlink**, not a symlink — the kernel reads it before symlinks resolve. + +## Filesystem & Boot + +### `fstab` + +Full btrfs-based layout using labels. All major mounts use `noatime,compress=lzo`. + +| Label | Mount | Notes | +| ----------- | --------------------------- | ------------------------------------ | +| `EFI-BLUE` | `/boot` | vfat EFI partition | +| `Archlinux` | `/toplevel` | btrfs root, all subvolumes live here | +| `Archlinux` | `/home/vacation` | `@vacation` subvolume | +| `Archlinux` | `~/.local/share/lutris` | `@lutris` — games isolated | +| `Archlinux` | `~/.local/share/Steam` | `@steam` — Steam isolated | +| `Archlinux` | `/var/lib/postgres` | `@postgres` | +| `Archlinux` | `~/.cache` | `@.cache` | +| `Archlinux` | `/var/lib/docker` | `@docker` | +| `Archlinux` | `/swap` | `@swap` subvolume | +| `Data2TB` | `/media/data` | secondary data disk | +| `Data2TB` | `~/.ollama` | Ollama model storage | +| `Data4TB` | `/media/data4tb` | tertiary data disk | +| `Data4TB` | `/plex` and `~/Videos/plex` | Plex media (double-mounted) | +| `External` | `/media/external` | exfat external drive, noauto | + +Swap is a swapfile at `/swap/swapfile`. + +### `mkinitcpio.conf` + +- **MODULES**: `fs_btrfs amdgpu` — preloaded for btrfs root and AMD GPU early KMS. +- **HOOKS**: `base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems resume` + — notably includes `resume` for hibernation support. +- `MODULES_DECOMPRESS=yes` — decompress kernel modules in initramfs for faster boot. + +### `boot/EFI/CLOVER/` + +CLOVER EFI bootloader config. The pre-commit hook syncs `config.plist` to `/boot/EFI/CLOVER/config.plist` on commit. + +## Display + +### `X11/xorg.conf.d/settings.conf` + +`DontVTSwitch=False` — allows VT switching from X11. + +### `sddm.conf.d/settings.conf` + +SDDM display manager config. Key settings: + +- Theme: `breeze` +- `EnableHiDPI=true` (X11 and Wayland) +- `Numlock=on` +- Autologin disabled + +## System Services + +### `systemd/sleep.conf.d/hibernate.conf` + +- Hibernate mode: `shutdown` (writes RAM to swap, powers off) +- `HibernateDelaySec=22h` — suspend-then-hibernate after 22h +- All sleep modes allowed: suspend, hibernate, hybrid, suspend-then-hibernate. + +### `systemd/system.conf.d/watchdog.conf` + +- `RuntimeWatchdogSec=3min` — kernel watchdog resets if systemd stalls +- `RebootWatchdogSec=15` — reboot watchdog on system reboot + +### `systemd/system/` — Root system units + +| Unit | Purpose | +| ------------------------- | -------------------------------------------------------------------------------------------------------- | +| `system-snapshot.service` | Runs `~/bin/system-snapshot` as root | +| `system-snapshot.timer` | Triggers hourly, persistent | +| `btrfs-scrub@.service` | Template unit; `%i` = volume name (Archlinux/Data2TB/Data4TB). Nice=19, IO idle priority. 30min timeout. | +| `btrfs-scrub@.timer` | Monthly, randomized by up to 1 week | +| `memreserver.service` | Runs before sleep (`Before=sleep.target`). Frees RAM so GPU VRAM can be evacuated on suspend. | + +## Network + +### `resolvconf.conf` + +- DNS: `1.1.1.1 8.8.8.8` (Cloudflare + Google) +- Blacklists Brazilian ISP DNS servers (`2804:14d:*`, `192.168.*`) and `wlp4s0` interface DNS + +### `geoclue/geoclue.conf` + +Geolocation daemon config. Uses Mozilla Location Service (`location.services.mozilla.com`). +Firefox and GNOME shell are whitelisted for location access. diff --git a/doc/structure.md b/doc/structure.md new file mode 100644 index 0000000..6d1de08 --- /dev/null +++ b/doc/structure.md @@ -0,0 +1,38 @@ +# Project Structure + +`interface` is a personal dotfiles and scripts repo for Arch Linux (primary) and macOS (secondary). +The repo is managed with **jujutsu** (`jj`). Do not make commits manually. + +## Directory Layout + +``` +interface/ +├── bin/ # Executables added to $PATH ($HOME/bin symlinks here) +│ └── git/ # Git hooks +├── boot/ # EFI/bootloader configs (CLOVER) +├── doc/ # Project documentation (this folder) +├── dotfiles/ # Config files symlinked into $HOME +│ ├── .config/ # XDG config (jj, Claude, VSCode, etc.) +│ └── lib*.sh # Shell libraries sourced at startup +├── etc/ # System configs symlinked into /etc +├── priv/ # Private/sensitive configs (not detailed here) +└── src/ # Go source for compiled tools + ├── forex/ + └── presentvalue/ +``` + +## Absolute-path symlinks + +Some symlinks reference `/home/vacation/` or `/Users/thom.ribeiro/`. They only work in the +original environment. When cloning elsewhere, update the prefix to your home directory. + +## How dotfiles are loaded + +1. `.bashrc` sets `$DOTFILES=$HOME/Desktop/interface/dotfiles` and sources: + - `lib.sh` — shell function library (logging, OS detection, etc.) + - `lib-git-prompt.sh` — git PS1 helpers, exports `$GIT_BRANCH` + - `lib-prompt.sh` — prompt construction + - `.aliases.sh` — cross-platform aliases + - `.aliases.linux.sh` or `.aliases.mac.sh` — OS-specific aliases +2. `.bash_env` sets `$BASH_ENV` so non-interactive scripts pick up `lib.sh` functions. + Scripts check `$BASH_ENV` is set and fail early if not. diff --git a/doc/workflows.md b/doc/workflows.md new file mode 100644 index 0000000..1352339 --- /dev/null +++ b/doc/workflows.md @@ -0,0 +1,130 @@ +# Workflows & Tooling + +## Commit workflow — `lazy-git` / `lazy-jujutsu` + +Both scripts enforce [Conventional Commits](https://www.conventionalcommits.org/) via `commitlint` +and spell-check the commit message with `cspell`. Invoked as: + +``` +lg # aliased to lazy-git / lazy-jujutsu +``` + +Colons in `type` and `scope` are stripped automatically. +If `scope` is a single character, the format collapses to `type: subject` (no parens). + +**`lazy-git` flow:** + +1. If files are already staged, commits those. Otherwise runs `git add --all`. +2. Builds `type(scope): subject` message, validates with `commitlint` + `cspell`. +3. Commits with `--no-verify` (hook already ran via pre-commit, or skipped intentionally). +4. If `$PWD`'s basename matches `$PUSH_REPOS`, pushes to origin. Sets upstream automatically if missing. +5. Prints `git status --short`. + +**`lazy-jujutsu` flow:** + +1. Same message construction and validation. +2. Finds the nearest ancestor bookmark via `jj log`. Updates the bookmark to `@` (unless it's `main`/`master`). +3. Runs `jj commit`. +4. If repo matches `$PUSH_REPOS`, runs `jj git push`. + +Config fallback: looks for `.commitlintrc.yml` and `.cspell.config.yml` in `$PWD`, falls back to `~/` defaults. + +**Globals consumed:** + +- `$PUSH_REPOS` — space-separated list of repo basenames to auto-push (set in `.bashrc`) +- `$GIT_BRANCH` — exported by `lib-git-prompt.sh` via `PROMPT_COMMAND` / `__git_ps1` +- `$BASH_ENV` — must be set; sources `lib.sh` functions + +## Pre-commit hook (`bin/git/interface-hook-pre-commit`) + +Installed on the `interface` and `priv` repos. Runs on every `git commit`: + +1. `cspell --unique .` — spellchecks the entire repo. +2. `install_config_plist` — if `boot/EFI/CLOVER/config.plist` changed, copies it to `/boot/EFI/CLOVER/config.plist` (Linux only, skipped on macOS). + +## System update — `system-up` + +Full Arch Linux update orchestrator. Run manually, interactive. + +``` +system-up +``` + +1. Prompts to snapshot `@root` (10s timeout, default skip). +2. Stops `postgresql` service. +3. Optionally runs `btrfs check` on Data2TB and Data4TB (prompts to unmount first). +4. Checks if PostgreSQL has a major version bump in AUR; if so, runs `pg_upgrade` automatically. +5. Runs `mise use --global` to update Node, Go, yarn, npm and reinstalls missing npm globals. +6. Updates Arch packages in order: `archlinux-keyring` → `linux*` → repo packages (non-linux) → AUR packages. +7. Starts `postgresql`, refreshes collations on all known databases. +8. Cleans up old postgres data dirs if a major migration happened. +9. Prompts to `kexec` into the new kernel (fast reboot, no POST). +10. Kills background `sudo -v` refresh loop. + +## Snapshot workflow + +### Interactive (`snapshot`) + +``` +snapshot [-n|--dry-run] +``` + +Prompts for: source subvolume under `/toplevel` → snapshot name (default `name-YYYY-MM-DD[-ro]`) → read-only flag. + +### Automated (`system-snapshot`) + +Called by `system-snapshot.service` (root, hourly timer). Takes target subvolume names as arguments, +creates read-only snapshots named `target-YYYY-MM-DD-HH-ro`, trims to keep max 50 per target. + +## Btrfs scrub + +### Manual (`btrfs-scrub`) + +``` +btrfs-scrub [cancel] +``` + +Maps volume labels to mount points and runs `btrfs scrub start` (or `cancel`). + +### Automated + +`btrfs-scrub@.service` / `btrfs-scrub@.timer` — monthly, randomized ±1 week, IO idle priority. + +## User systemd services (dotfiles/.config/systemd/user/) + +| Service | Timer | Purpose | +| ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------- | +| `app-inhibit-sleep` | always-on | Watches `pacman`, `yay`, and Firefox audio via `playerctl`; calls `systemd-inhibit` to block idle sleep | +| `forex@.service` | hourly | Fetches exchange rate for currency `%i`; reads env from `~/.config/t0/forex.env` | +| `game-audio-fix` | every minute | Corrects 5.1→2.0 audio routing for games | +| `docker-prune` | Saturdays 15–23h | `docker system prune` | +| `system-update-notification` | Sundays 11:00 | `notify-send` reminder to run system update | +| `ollama` | always-on | `ollama serve` with ROCm (AMD GPU), listens on `0.0.0.0:11434` for Docker bridge access | +| `xkbcomp` | on graphical session | Loads custom XKB keymap from `dotfiles/.config/keymap.xkb` | +| `xset-rate` | on default target | Sets key repeat: 140ms delay, 70ms interval | +| `dictation-shortcut` | always-on | Python keyboard hook on `KEY_F2` (Keychron Q10) for dictation | +| `docker` override | — | Sets `DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=false` so containers can reach host | + +## Prompt + +`lib-prompt.sh` builds PS1 via `make_ps1`: + +- Random 256-color ANSI color per session. +- Decorations: `~>` for known user, `#>` for root, `*>` in secondary color for unknown hostnames. +- Appends hostname if not in `$KNOWN_HOSTS`. +- Integrates with `lib-git-prompt.sh` (`__git_ps1`) for git/jj branch and dirty-state indicators. + +## Go tools — building + +```bash +compile-bin # run from repo root +``` + +Iterates `src/**/main.go`, builds each with `go build -race -mod=readonly` into `bin/`. + +## Code style + +- Shell: `shfmt` (run as last step after edits) +- Commits: Conventional Commits via `commitlint` +- Spellcheck: `cspell` +- VCS: **jujutsu** (`jj`) — do not make manual commits From 311733498b2768c2b31ec027028406bf486a9e4e Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Tue, 24 Mar 2026 21:04:08 -0300 Subject: [PATCH 02/23] misc(dotfiles/.bashrc): update PUSH_REPOS --- dotfiles/.bashrc | 2 +- dotfiles/.config/Claude/claude_desktop_config.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotfiles/.bashrc b/dotfiles/.bashrc index d1eb05e..17e0225 100644 --- a/dotfiles/.bashrc +++ b/dotfiles/.bashrc @@ -41,7 +41,7 @@ export GPG_TTY=$(tty) export XDG_RUNTIME_DIR export WAYLAND_DISPLAY # see lazy-git -export PUSH_REPOS="member-client interface priv hub-client server server-1 server-2 server-3 server-4 member-server shared go-athenahealth scheduling jail-mcp comms programming-problems" +export PUSH_REPOS="member-client go-common interface priv hub-client server server-1 server-2 server-3 server-4 member-server shared go-athenahealth scheduling jail-mcp comms programming-problems" # libs src_dotfile "lib.sh" "$LINENO" diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json index 433cc09..54f2530 100644 --- a/dotfiles/.config/Claude/claude_desktop_config.json +++ b/dotfiles/.config/Claude/claude_desktop_config.json @@ -17,9 +17,9 @@ "preferences": { "quickEntryShortcut": "off", "quickEntryDictationShortcut": "capslock", - "coworkScheduledTasksEnabled": false, - "ccdScheduledTasksEnabled": false, - "sidebarMode": "chat", + "coworkScheduledTasksEnabled": true, + "ccdScheduledTasksEnabled": true, + "sidebarMode": "task", "coworkWebSearchEnabled": true } } \ No newline at end of file From 44865e4e02b88bbcbf8305803430c7a010e92f80 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Thu, 26 Mar 2026 00:23:40 -0300 Subject: [PATCH 03/23] fix(bin/system-up): simplify mise update --- bin/system-up | 14 ++------------ dotfiles/.config/Claude/claude_desktop_config.json | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/bin/system-up b/bin/system-up index 5ece32b..5158c73 100755 --- a/bin/system-up +++ b/bin/system-up @@ -215,20 +215,10 @@ mise_install_globals() { mise use --global npm@latest # reinstall missing npm globals - local packages=(cspell cspell@latest prettier prettier@latest commitlint @commitlint/cli@latest commitlint/config-conventional @commitlint/config-conventional@latest) + local packages=(cspell@latest prettier@latest @commitlint/cli@latest @commitlint/config-conventional@latest) for ((i = 0; i < ${#packages[@]}; i++)); do - if ((i % 2 != 0)); then - continue - fi - - name="${packages[i]}" - version="${packages[i + 1]}" - - # command -v reports false positives on aliases - if ! which "$name" >/dev/null 2>&1; then - npm install --global "$version" - fi + npm install --global "${packages[i]}" done # export PATH="$PATH:$HOME/.local/share/mise/installs/node/$(node -v | tr -d v)/bin" diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json index 54f2530..7a295db 100644 --- a/dotfiles/.config/Claude/claude_desktop_config.json +++ b/dotfiles/.config/Claude/claude_desktop_config.json @@ -19,7 +19,7 @@ "quickEntryDictationShortcut": "capslock", "coworkScheduledTasksEnabled": true, "ccdScheduledTasksEnabled": true, - "sidebarMode": "task", + "sidebarMode": "chat", "coworkWebSearchEnabled": true } } \ No newline at end of file From 18ace898ae41c614e216698d096d0ba6d0d569f0 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 11:48:23 -0300 Subject: [PATCH 04/23] fix(bin/download-youtube-id): de-dupe extension --- bin/download-youtube-id | 41 +++++++------------ doc/ideas.md | 14 +++++++ .../.config/Claude/claude_desktop_config.json | 4 +- 3 files changed, 30 insertions(+), 29 deletions(-) create mode 100644 doc/ideas.md diff --git a/bin/download-youtube-id b/bin/download-youtube-id index 3fab911..58499bb 100755 --- a/bin/download-youtube-id +++ b/bin/download-youtube-id @@ -83,7 +83,7 @@ prepare_temp() { } download() { - local format_id=$1 + local format_id=$1 prefix=${2:-} if [ ! "$format_id" ]; then return @@ -91,38 +91,25 @@ download() { # downloading big videos can max out /tmp # ejs:github related to automated captcha challenges using deno - if ! yt-dlp -f "$format_id" "$vid_id" --output "%(title)s-%(release_date)s.%(ext)s" --remote-components ejs:github --paths "temp:/plex/tmp"; then + if ! yt-dlp -f "$format_id" "$vid_id" --output "${prefix}%(title)s-%(release_date)s.%(ext)s" --remote-components ejs:github --paths "temp:/plex/tmp"; then fatal "$LINENO" "yt-dlp error" fi } prompt_audio_format() { msgln note: high quality video has no audio, choose audio now - msgln choose an audio format: + msgln "type an audio format extension (ID EXT columns) e.g. 251 webm:" - read -r - audio_fmt="$REPLY" - msgln audio extension: \(see column EXT\) - - read -r - audio_ext="$REPLY" + read -r audio_fmt audio_ext } prompt_video_format() { - msgln Choose a video format: - msgln 4k: look for mp4 3840x2160 - msgln 2k: look for mp4 2560x1440 - msgln Leave blank to download only audio - - read -r - video_fmt="$REPLY" + msgln "type a video format extension (ID EXT columns) e.g. 137 mp4:" + msgln "4k: look for mp4 3840x2160" + msgln "2k: look for mp4 2560x1440" + msgln "Leave blank to download only audio" - if [ "$video_fmt" ]; then - msgln Video extension: \(see column EXT\) - - read -r - video_ext="$REPLY" - fi + read -r video_fmt video_ext } validate_formats() { @@ -159,7 +146,7 @@ mux() { # ffmpeg -i ./*."$audio_ext" -i ./*."$video_ext" -crf "$crf" -ac 2 "$name.mp4" # gpu using va - ffmpeg -vaapi_device /dev/dri/renderD128 -i ./*."$audio_ext" -i ./*."$video_ext" \ + ffmpeg -vaapi_device /dev/dri/renderD128 -i ./audio-*."$audio_ext" -i ./video-*."$video_ext" \ -c:v hevc_vaapi -vf format=nv12,hwupload \ -qp "$crf" -ac 2 "$name.mp4" mv "$name.mp4" "$out_dir" @@ -174,7 +161,7 @@ convert_MP3() { fi local audio_file - audio_file=$(find . -maxdepth 1 -type f | head -1) + audio_file=$(find . -maxdepth 1 -name "audio-*" -type f | head -1) if [ ! "$audio_file" ]; then fatal "$LINENO" "no audio file found" @@ -207,8 +194,8 @@ prompt_video_format validate_formats -download "$audio_fmt" -download "$video_fmt" +download "$audio_fmt" "audio-" +download "$video_fmt" "video-" mux "$title-$vid_id" convert_MP3 "$title-$vid_id" @@ -218,4 +205,4 @@ cleanup_tmp cd "$initial_working_dir" -info $LINENO ok "$out_dir/$title-$vid_id.mp4" +log $LINENO ok "$out_dir/$title-$vid_id.mp4" diff --git a/doc/ideas.md b/doc/ideas.md new file mode 100644 index 0000000..f22311d --- /dev/null +++ b/doc/ideas.md @@ -0,0 +1,14 @@ +# open questions + +Things that jump out: +The `BASHENV‘patterniscleverbutfragile—everyscripthasthat‘checkdependencies‘boilerplateatthetop.Itworks,butitmeansifyouevermoveorrename‘lib.sh‘,everyscriptbreakssilentlyatruntimeratherthanatsourcetime.Thecheckalsoonlytestsif‘BASH_ENV` pattern is clever but fragile — every script has that `check_dependencies` boilerplate at the top. It works, but it means if you ever move or rename `lib.sh`, every script breaks silently at runtime rather than at source time. The check also only tests if ` +BASHE​NV‘patterniscleverbutfragile—everyscripthasthat‘checkd​ependencies‘boilerplateatthetop.Itworks,butitmeansifyouevermoveorrename‘lib.sh‘,everyscriptbreakssilentlyatruntimeratherthanatsourcetime.Thecheckalsoonlytestsif‘BASH_ENV` is _set_, not if `lib.sh` actually _loaded_ — so a wrong path in `.bash_env` would still pass the check. + +The binaries committed directly to bin/ (typo, dir-rename) are interesting — they're ELF x86-64 blobs with no source in the repo. That's a trust and portability issue, and if you ever need to rebuild them or debug them, there's no path forward from within the repo. +fstab as a hardlink is a smart workaround but it means the repo and /etc/fstab are silently diverging any time you edit one without the other. There's no enforcement. +The system-up script has a postgres_up function that calls extract_version and then does [ "$major_sub" -ge 1 ] — but extract_version returns strings like "15." (with a trailing dot from the regex). The bc subtraction would work, but the comparison on the result of bc gives a float like "1.0", and [ "1.0" -ge 1 ] is actually a bash integer comparison that would fail on the .0 suffix. Worth checking if that code path has actually been exercised recently. + +Genuine questions: +The PUSH_REPOS list in .bashrc has interface in it — but the repo uses jujutsu, not git. lazy-git checks $PUSH_REPOS and calls git push. Does lazy-git ever actually run against this repo, or is lazy-jujutsu always the one used here? +The ollama service listens on 0.0.0.0 so Docker containers can reach it — what are you running in Docker that talks to Ollama? Local LLM tooling for work, or something else? +And the dictation-keyboard-hook on KEY_F2 of the Keychron Q10 — what does it actually do? Does it pipe audio to a Whisper model, or is it hooking into some other dictation stack? diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json index 7a295db..433cc09 100644 --- a/dotfiles/.config/Claude/claude_desktop_config.json +++ b/dotfiles/.config/Claude/claude_desktop_config.json @@ -17,8 +17,8 @@ "preferences": { "quickEntryShortcut": "off", "quickEntryDictationShortcut": "capslock", - "coworkScheduledTasksEnabled": true, - "ccdScheduledTasksEnabled": true, + "coworkScheduledTasksEnabled": false, + "ccdScheduledTasksEnabled": false, "sidebarMode": "chat", "coworkWebSearchEnabled": true } From 10dd325cd3547c8fa5ba16d0931cef3190644e69 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:08:53 -0300 Subject: [PATCH 05/23] feat(aliases): rework jj aliases and functions --- AGENTS.md | 8 ++++++ dotfiles/.aliases.sh | 8 +++--- dotfiles/.cspell.config.yml | 2 +- dotfiles/.functions.sh | 50 +++++++++++++++++++++++++++++-------- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fc7b3d9..4a2229f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,14 @@ Most of the scripts have no tests, we gotta run them to test. Keep comments short and sweet, don't document obvious code. **Formatting:** We use `shfmt`. +When moving an alias foo to a function add: +```sh +# todo: remove +unalias foo 2>/dev/null +foo() { +``` +this fixes weird bash errors when an alias and a function conflict. +separate functions neatly using #---------------- ## Misc diff --git a/dotfiles/.aliases.sh b/dotfiles/.aliases.sh index 67a52f8..25ce9ce 100644 --- a/dotfiles/.aliases.sh +++ b/dotfiles/.aliases.sh @@ -86,7 +86,6 @@ alias gcpa='git cherry-pick --abort' alias gcpc='git cherry-pick --continue' alias gcs='git commit -S' alias gcsm='git commit -s -m' -alias gd='jj diff' alias gdca='git diff --cached' alias gdct='git describe --tags `git rev-list --tags --max-count=1`' alias gdcw='git diff --cached --word-diff' @@ -240,9 +239,13 @@ alias misc-="lg misc -" alias chore-="lg chore -" # jujutsu +alias j="jj" alias jgp="jj git push" +alias jgf="jj git fetch" alias jn="jj new" -alias gss="jss" +alias jsq="jj squash -m" +alias jre="jj resolve" +alias jbf="jj bookmark forget" # general alias la='ls -A' @@ -259,7 +262,6 @@ alias part='partutil' alias srit='source $HOME/.bashrc && clear' alias dirs='dirs -v' alias stat='stat -L' -alias j='jobs' alias f='fg' alias g='grep -Eie' alias ping='ping -c 1' diff --git a/dotfiles/.cspell.config.yml b/dotfiles/.cspell.config.yml index a5241f4..252dcf1 100644 --- a/dotfiles/.cspell.config.yml +++ b/dotfiles/.cspell.config.yml @@ -113,7 +113,7 @@ words: - IOUSB - IPIC - iterm - - jjrb + - jrb - kaue - kazuo - kdesu diff --git a/dotfiles/.functions.sh b/dotfiles/.functions.sh index 2316f7c..bc38c9c 100644 --- a/dotfiles/.functions.sh +++ b/dotfiles/.functions.sh @@ -131,10 +131,26 @@ yad() { #- - - - - - - - - - - +# todo: remove +unalias gd 2>/dev/null + +gd() { + if ! command jj root &>/dev/null; then + command git diff "$@" + return $? + fi + + command jj diff "$@" +} + +#- - - - - - - - - - - + # when there's no repo, call ls instead -jss() { +# todo: remove +unalias gss 2>/dev/null +gss() { if ! command jj root &>/dev/null; then - _git_gss "$@" + _git_gss return $? fi @@ -396,10 +412,10 @@ __jj_bookmark_set() { #---------------- # jj bookmark set on @ -jjb() { +jb() { if [[ $# == 0 ]]; then - echo "jjb bookmark set on @" - echo "'jjb foobar' jj bookmark set foobar --revision @" + echo "jb bookmark set on @" + echo "'jb foobar' jj bookmark set foobar --revision @" return fi @@ -409,10 +425,10 @@ jjb() { #---------------- # jj bookmark set on @- -jjb-() { +jb-() { if [[ $# == 0 ]]; then - echo "jjb- bookmark set on @-" - echo "'jjb- foobar' jj bookmark set foobar --revision @- --allow-backwards" + echo "jb- bookmark set on @-" + echo "'jb- foobar' jj bookmark set foobar --revision @- --allow-backwards" return fi @@ -421,8 +437,20 @@ jjb-() { #---------------- +# jj bookmark track --remote=origin +jbt() { + if [[ $# == 0 ]]; then + echo "jbt — jj bookmark track --remote=origin" + return + fi + + jj bookmark track "$1" --remote=origin +} + +#---------------- + # jj rebase --source $1 --destination $2 -jjrb() { +jrb() { if [[ $# != 2 ]]; then echo "jj rebase --source \$1 --destination \$2" return @@ -474,9 +502,9 @@ __npc() { local branch=$1 backwards_flag="$2" today_date=$(date +"%b-%d" | tr '[:upper:]' '[:lower:]') prefix=px if [ "$backwards_flag" == "-" ]; then - jjb- "$prefix-$1-$today_date" + jb- "$prefix-$1-$today_date" else - jjb "$prefix-$1-$today_date" + jb "$prefix-$1-$today_date" fi } From 4f57102a080cb00e679dd34636816290268d42ed Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:30:23 -0300 Subject: [PATCH 06/23] feat: bin/setup, workflows --- .github/workflows/shellcheck.yml | 19 ++++ .github/workflows/shfmt.yml | 19 ++++ bin/setup | 114 +++++++++++++++++++ dotfiles/.config/code-oss/User/settings.json | 3 +- 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/shellcheck.yml create mode 100644 .github/workflows/shfmt.yml create mode 100755 bin/setup diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..61eb087 --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,19 @@ +name: shellcheck + +on: + pull_request: + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: install tools + run: bin/setup + + - name: run shellcheck + run: | + find bin dotfiles -type f \ + | xargs grep -lE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs shellcheck diff --git a/.github/workflows/shfmt.yml b/.github/workflows/shfmt.yml new file mode 100644 index 0000000..054d84f --- /dev/null +++ b/.github/workflows/shfmt.yml @@ -0,0 +1,19 @@ +name: shfmt + +on: + pull_request: + +jobs: + shfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: install tools + run: bin/setup + + - name: run shfmt + run: | + find bin dotfiles -type f \ + | xargs grep -lE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs shfmt -i 2 -ln bash -d diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..86461fa --- /dev/null +++ b/bin/setup @@ -0,0 +1,114 @@ +#! /usr/bin/env bash +# Copyright 2025 Raphael Thomazella. All rights reserved. +# Use of this source code is governed by the BSD-3-Clause +# license that can be found in the LICENSE file and online +# at https://opensource.org/license/BSD-3-clause. +# +# Installs development tools required by this repo. +# Self-contained: does not require $BASH_ENV. +# Interactive shell: prints install instructions and exits. +# Non-interactive: downloads and installs binaries from GitHub releases. + +set -euo pipefail +shopt -s globstar + +### vars and functions ### + +msgln() { echo -e "$*"; } +log() { echo -e "INFO ($0:${1}) ${*:2}" >&2; } +err() { echo -e "ERROR ($0:${1})" >&2; } +fatal() { err "$1" "${*:2}"; exit 1; } + +trap 'err $LINENO' ERR + +readonly install_dir="/usr/local/bin" + +print_manual_instructions() { + msgln "Some tools are missing. Please install them manually:" + msgln + msgln " shfmt: https://github.com/mvdan/sh/releases/latest" + msgln " shellcheck: https://github.com/koalaman/shellcheck/releases/latest" + msgln + msgln "On Arch: sudo pacman -S shfmt shellcheck" + msgln "On macOS: brew install shfmt shellcheck" +} + +latest_version() { + local repo=$1 + + curl -sf "https://api.github.com/repos/$repo/releases/latest" \ + | grep '"tag_name"' \ + | sed -E 's/.*"tag_name": "(.*)".*/\1/' +} + +install_shfmt() { + local version os arch url tmp + + log $LINENO "fetching latest shfmt version" + version=$(latest_version mvdan/sh) + + os=$(uname -s | tr '[:upper:]' '[:lower:]') + arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + url="https://github.com/mvdan/sh/releases/download/${version}/shfmt_${version}_${os}_${arch}" + tmp=$(mktemp) + + log $LINENO "downloading shfmt $version" + curl -sfL "$url" -o "$tmp" + chmod +x "$tmp" + sudo mv "$tmp" "$install_dir/shfmt" + log $LINENO "shfmt installed to $install_dir/shfmt" +} + +install_shellcheck() { + local version os arch tarball url tmp_dir + + log $LINENO "fetching latest shellcheck version" + version=$(latest_version koalaman/shellcheck) + + os=$(uname -s | tr '[:upper:]' '[:lower:]') + arch=$(uname -m) + tarball="shellcheck-${version}.${os}.${arch}.tar.xz" + url="https://github.com/koalaman/shellcheck/releases/download/${version}/${tarball}" + tmp_dir=$(mktemp -d) + + log $LINENO "downloading shellcheck $version" + curl -sfL "$url" | tar -xJ -C "$tmp_dir" + sudo mv "$tmp_dir/shellcheck-${version}/shellcheck" "$install_dir/shellcheck" + rm -rf "$tmp_dir" + log $LINENO "shellcheck installed to $install_dir/shellcheck" +} + +install_tool() { + local tool=$1 install_fn=$2 + + if command -v "$tool" >/dev/null 2>&1; then + log $LINENO "$tool already installed" + return + fi + + "$install_fn" +} + +### script ### + +missing=false + +command -v shfmt >/dev/null 2>&1 || missing=true +command -v shellcheck >/dev/null 2>&1 || missing=true + +if ! $missing; then + log $LINENO "all tools already installed" + exit 0 +fi + +# interactive shell: just tell the user +if [[ $- == *i* ]]; then + print_manual_instructions + exit 0 +fi + +# non-interactive: download and install +install_tool shfmt install_shfmt +install_tool shellcheck install_shellcheck + +msgln "setup complete" diff --git a/dotfiles/.config/code-oss/User/settings.json b/dotfiles/.config/code-oss/User/settings.json index b8b20e0..34f4f80 100644 --- a/dotfiles/.config/code-oss/User/settings.json +++ b/dotfiles/.config/code-oss/User/settings.json @@ -148,7 +148,8 @@ "go.vetOnSave": "off", "go.formatTool": "gofumpt", "gopls": { - "ui.semanticTokens": true + "ui.semanticTokens": true, + "build.buildFlags": ["-buildvcs=false"] }, "go.toolsManagement.autoUpdate": true, // From ec655ed0f22d01faf68405fb9a689ec207cbe2b7 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:44:54 -0300 Subject: [PATCH 07/23] fix(bin/setup): allow running as root --- bin/setup | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/setup b/bin/setup index 86461fa..05953c3 100755 --- a/bin/setup +++ b/bin/setup @@ -23,6 +23,14 @@ trap 'err $LINENO' ERR readonly install_dir="/usr/local/bin" +run_privileged() { + if [ "$(id -u)" = "0" ]; then + "$@" + else + sudo "$@" + fi +} + print_manual_instructions() { msgln "Some tools are missing. Please install them manually:" msgln @@ -55,7 +63,7 @@ install_shfmt() { log $LINENO "downloading shfmt $version" curl -sfL "$url" -o "$tmp" chmod +x "$tmp" - sudo mv "$tmp" "$install_dir/shfmt" + run_privileged mv "$tmp" "$install_dir/shfmt" log $LINENO "shfmt installed to $install_dir/shfmt" } @@ -73,7 +81,7 @@ install_shellcheck() { log $LINENO "downloading shellcheck $version" curl -sfL "$url" | tar -xJ -C "$tmp_dir" - sudo mv "$tmp_dir/shellcheck-${version}/shellcheck" "$install_dir/shellcheck" + run_privileged mv "$tmp_dir/shellcheck-${version}/shellcheck" "$install_dir/shellcheck" rm -rf "$tmp_dir" log $LINENO "shellcheck installed to $install_dir/shellcheck" } From 42b3fd26a3fa683148da7fac0a944a420e1bb10c Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:54:05 -0300 Subject: [PATCH 08/23] misc: lint and format --- .github/workflows/shellcheck.yml | 6 +++--- .github/workflows/shfmt.yml | 6 +++--- AGENTS.md | 1 + bin/lazy-jujutsu | 2 +- bin/setup | 11 +++++++---- dotfiles/.bash_env | 1 + dotfiles/.bash_profile | 1 + dotfiles/.functions.linux.sh | 3 +-- dotfiles/lib-git-prompt.sh | 21 ++++++++++++--------- run | 32 ++++++++++++++++++++++++++++++++ 10 files changed, 62 insertions(+), 22 deletions(-) create mode 100755 run diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 61eb087..0dc8284 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -14,6 +14,6 @@ jobs: - name: run shellcheck run: | - find bin dotfiles -type f \ - | xargs grep -lE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ - | xargs shellcheck + find bin dotfiles -type f -print0 \ + | xargs -0 grep -lZE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs -0 shellcheck -x diff --git a/.github/workflows/shfmt.yml b/.github/workflows/shfmt.yml index 054d84f..986bb43 100644 --- a/.github/workflows/shfmt.yml +++ b/.github/workflows/shfmt.yml @@ -14,6 +14,6 @@ jobs: - name: run shfmt run: | - find bin dotfiles -type f \ - | xargs grep -lE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ - | xargs shfmt -i 2 -ln bash -d + find bin dotfiles -type f -print0 \ + | xargs -0 grep -lZE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs -0 shfmt -i 2 -ln bash -d diff --git a/AGENTS.md b/AGENTS.md index 4a2229f..62b099c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,3 +32,4 @@ avoid single letter vars if their scope is not small. when we refactor, minimize renames unless asked for. run formatter as last step after making code changes. this is a jujutsu repo and do not make commits. +lib-git-prompt.sh has been vendored and we can modify it. \ No newline at end of file diff --git a/bin/lazy-jujutsu b/bin/lazy-jujutsu index 4771396..7b3f42c 100755 --- a/bin/lazy-jujutsu +++ b/bin/lazy-jujutsu @@ -97,7 +97,7 @@ push() { debug $LINENO "$base_pwd matches \$PUSH_REPOS" - command jj git push --quiet + command jj git push --quiet } ### script ### diff --git a/bin/setup b/bin/setup index 05953c3..9090a18 100755 --- a/bin/setup +++ b/bin/setup @@ -17,7 +17,10 @@ shopt -s globstar msgln() { echo -e "$*"; } log() { echo -e "INFO ($0:${1}) ${*:2}" >&2; } err() { echo -e "ERROR ($0:${1})" >&2; } -fatal() { err "$1" "${*:2}"; exit 1; } +fatal() { + err "$1" "${*:2}" + exit 1 +} trap 'err $LINENO' ERR @@ -44,9 +47,9 @@ print_manual_instructions() { latest_version() { local repo=$1 - curl -sf "https://api.github.com/repos/$repo/releases/latest" \ - | grep '"tag_name"' \ - | sed -E 's/.*"tag_name": "(.*)".*/\1/' + curl -sf "https://api.github.com/repos/$repo/releases/latest" | + grep '"tag_name"' | + sed -E 's/.*"tag_name": "(.*)".*/\1/' } install_shfmt() { diff --git a/dotfiles/.bash_env b/dotfiles/.bash_env index 86ee844..2f23389 100644 --- a/dotfiles/.bash_env +++ b/dotfiles/.bash_env @@ -1,4 +1,5 @@ #! /usr/bin/env bash +# shellcheck disable=SC1091 # uninteractive bash env # BASH_ENV must be set to this script in bash config files diff --git a/dotfiles/.bash_profile b/dotfiles/.bash_profile index 4195884..3c92fde 100644 --- a/dotfiles/.bash_profile +++ b/dotfiles/.bash_profile @@ -1,4 +1,5 @@ #! /usr/bin/env bash +# shellcheck disable=SC1091 if [ -f "$HOME/.bashrc" ]; then # shellcheck source=.bashrc diff --git a/dotfiles/.functions.linux.sh b/dotfiles/.functions.linux.sh index ac5d53f..e47f71d 100644 --- a/dotfiles/.functions.linux.sh +++ b/dotfiles/.functions.linux.sh @@ -209,7 +209,7 @@ compress() { msg "creating tar.7z archive: $out" # tar stream → 7za via stdin (your exact pattern) - if ! tar -cf - "$src" | 7za a -si -mx=7 "$out" > /dev/null; then + if ! tar -cf - "$src" | 7za a -si -mx=7 "$out" >/dev/null; then err $LINENO "tar.7z compression failed" return 1 fi @@ -221,4 +221,3 @@ compress() { msg "done: $out" } - diff --git a/dotfiles/lib-git-prompt.sh b/dotfiles/lib-git-prompt.sh index cf032d5..a2e79c7 100644 --- a/dotfiles/lib-git-prompt.sh +++ b/dotfiles/lib-git-prompt.sh @@ -3,8 +3,11 @@ # bash/zsh git prompt support # # Copyright (C) 2006,2007 Shawn O. Pearce +# Copyright (C) 2025 R. Thomazella # Distributed under the GNU General Public License, version 2.0. # +# This file is a modified fork of the original git-prompt.sh. +# # This script allows you to see repository status in your prompt. # # To enable: @@ -121,7 +124,7 @@ __git_ps1_show_upstream() { fi ;; svn-remote.*.url) - svn_remote[${#svn_remote[@]} + 1]="$value" + svn_remote[${#svn_remote[@]}+1]="$value" svn_url_pattern="$svn_url_pattern\\|$value" upstream=svn+git # default upstream is SVN if available, else git ;; @@ -145,21 +148,21 @@ __git_ps1_show_upstream() { # get the upstream from the "git-svn-id: ..." in a commit message # (git-svn uses essentially the same procedure internally) local -a svn_upstream - svn_upstream=($(git log --first-parent -1 \ - --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null)) + mapfile -t svn_upstream < <(git log --first-parent -1 \ + --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null) if [[ 0 -ne ${#svn_upstream[@]} ]]; then - svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]} - svn_upstream=${svn_upstream%@*} + local svn_upstream_str=${svn_upstream[${#svn_upstream[@]}-2]} + svn_upstream_str=${svn_upstream_str%@*} local n_stop="${#svn_remote[@]}" for ((n = 1; n <= n_stop; n++)); do - svn_upstream=${svn_upstream#${svn_remote[$n]}} + svn_upstream_str=${svn_upstream_str#"${svn_remote[$n]}"} done - if [[ -z "${svn_upstream[0]}" ]]; then + if [[ -z "$svn_upstream_str" ]]; then # default branch name for checkouts with no layout: upstream=${GIT_SVN_ID:-git-svn} else - upstream=${svn_upstream#/} + upstream=${svn_upstream_str#/} fi elif [[ "svn+git" == "$upstream" ]]; then upstream="@{upstream}" @@ -331,7 +334,7 @@ __git_ps1() { local printf_format=' (%s)' if git describe --contains --all HEAD >/dev/null 2>&1; then GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) - GIT_UPSTREAM="$(git rev-parse --abbrev-ref "$GIT_BRANCH"@{upstream} 2>/dev/null)" + GIT_UPSTREAM="$(git rev-parse --abbrev-ref "${GIT_BRANCH}@{upstream}" 2>/dev/null)" export GIT_BRANCH export GIT_UPSTREAM fi diff --git a/run b/run new file mode 100755 index 0000000..3a7b258 --- /dev/null +++ b/run @@ -0,0 +1,32 @@ +#! /usr/bin/env bash + +set -euo pipefail +shopt -s globstar +trap 'echo fatal: $0:$LINENO >&2' ERR + +cmd="${1:-help}" + +case "$cmd" in +# -- run shfmt on all bash scripts +format) + find bin dotfiles -type f -print0 \ + | xargs -0 grep -lZE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs -0 shfmt -i 2 -ln bash -w + ;; +# -- run shellcheck on all bash scripts +lint) + find bin dotfiles -type f -print0 \ + | xargs -0 grep -lZE '^#! ?/usr/bin/env bash|^#!/usr/bin/env bash|^#! ?/bin/bash' \ + | xargs -0 shellcheck -x + ;; +# -- install required dev tools +setup) + bin/setup + ;; +help | *) + echo "Usage: ./run " + echo "" + echo "Commands:" + awk '/^# -- /{desc=substr($0, index($0,"-- ")+3)} /\)$/ && desc{cmd=$0; gsub(/[[:space:]()|*]/, "", cmd); printf " %-20s %s\n", cmd, desc; desc=""}' "$0" + ;; +esac From e1ac222a324c05316a4834f124163fe35c237035 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:58:45 -0300 Subject: [PATCH 09/23] misc: update license boilerplate --- LICENSE | 2 +- bin/app-inhibit-sleep | 2 +- bin/btrfs-scrub | 2 +- bin/bulk-curl-from-file | 2 +- bin/compile-bin | 2 +- bin/download-youtube-id | 2 +- bin/extract | 2 +- bin/game-audio-fix | 2 +- bin/hue | 4 ++-- bin/lazy-git | 2 +- bin/lazy-jujutsu | 2 +- bin/setup | 2 +- bin/snapshot | 2 +- bin/system-snapshot | 2 +- bin/system-up | 2 +- bin/template | 2 +- dotfiles/.aliases.sh | 2 -- dotfiles/lib.sh | 2 +- src/forex/main.go | 2 +- 19 files changed, 19 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index 0bc569a..6cdcfce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2018-present, Raphael Thomazella +Copyright (c) 2018-present, R. Thomazella All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/bin/app-inhibit-sleep b/bin/app-inhibit-sleep index 4485343..d41712b 100755 --- a/bin/app-inhibit-sleep +++ b/bin/app-inhibit-sleep @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2025 Raphael Thomazella. All rights reserved. +# Copyright 2025-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/btrfs-scrub b/bin/btrfs-scrub index 42f6e5e..10cd5b7 100755 --- a/bin/btrfs-scrub +++ b/bin/btrfs-scrub @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/bulk-curl-from-file b/bin/bulk-curl-from-file index c58cc41..67d010e 100755 --- a/bin/bulk-curl-from-file +++ b/bin/bulk-curl-from-file @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/compile-bin b/bin/compile-bin index df5957c..3204a22 100755 --- a/bin/compile-bin +++ b/bin/compile-bin @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2025 Raphael Thomazella. All rights reserved. +# Copyright 2025-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/download-youtube-id b/bin/download-youtube-id index 58499bb..185d92f 100755 --- a/bin/download-youtube-id +++ b/bin/download-youtube-id @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/extract b/bin/extract index f7dc1ad..a451322 100755 --- a/bin/extract +++ b/bin/extract @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/game-audio-fix b/bin/game-audio-fix index b144e14..a65ed9b 100755 --- a/bin/game-audio-fix +++ b/bin/game-audio-fix @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2025 Raphael Thomazella. All rights reserved. +# Copyright 2025-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/hue b/bin/hue index a6f2005..074b537 100755 --- a/bin/hue +++ b/bin/hue @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2018-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. @@ -8,7 +8,7 @@ # 🚀 2018 by Thomazella 🌙 # http://tazel.website # https://github.com/Thomazella -# reviewed by Thomazella on 2024 +# reviewed by R. Thomazella on 2024 # main changes were linter fixes and some # bad var names renamed for clarity # diff --git a/bin/lazy-git b/bin/lazy-git index 5cc700e..e90bc0f 100755 --- a/bin/lazy-git +++ b/bin/lazy-git @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/lazy-jujutsu b/bin/lazy-jujutsu index 7b3f42c..f8663a9 100755 --- a/bin/lazy-jujutsu +++ b/bin/lazy-jujutsu @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/setup b/bin/setup index 9090a18..8df1cc8 100755 --- a/bin/setup +++ b/bin/setup @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2025 Raphael Thomazella. All rights reserved. +# Copyright 2025-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/snapshot b/bin/snapshot index 87fdc90..016e3e5 100755 --- a/bin/snapshot +++ b/bin/snapshot @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/system-snapshot b/bin/system-snapshot index fd03c31..190e180 100755 --- a/bin/system-snapshot +++ b/bin/system-snapshot @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/system-up b/bin/system-up index 5158c73..bcc7f09 100755 --- a/bin/system-up +++ b/bin/system-up @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/bin/template b/bin/template index 25273dc..0ad9b5f 100755 --- a/bin/template +++ b/bin/template @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2025 Raphael Thomazella. All rights reserved. +# Copyright 2025-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/dotfiles/.aliases.sh b/dotfiles/.aliases.sh index 25ce9ce..a0df753 100644 --- a/dotfiles/.aliases.sh +++ b/dotfiles/.aliases.sh @@ -272,8 +272,6 @@ alias visudo="EDITOR='code -w' && command sudo visudo" alias shfmt="shfmt -i 2 -ln bash" alias shellcheck="shellcheck --color=auto -s bash" alias cat='bat --theme Monokai\ Extended\ Origin' -alias gppr='gpsup && git pull-request -b main --browse --assign thomazella' -alias gpprd='gpsup && git pull-request -b develop --browse --assign thomazella' alias t="cat" alias l="less" alias wget='wget -c' diff --git a/dotfiles/lib.sh b/dotfiles/lib.sh index 3dc4bfe..b643761 100644 --- a/dotfiles/lib.sh +++ b/dotfiles/lib.sh @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright 2024 Raphael Thomazella. All rights reserved. +# Copyright 2024-present R. Thomazella. All rights reserved. # Use of this source code is governed by the BSD-3-Clause # license that can be found in the LICENSE file and online # at https://opensource.org/license/BSD-3-clause. diff --git a/src/forex/main.go b/src/forex/main.go index f4d2749..5c146e2 100644 --- a/src/forex/main.go +++ b/src/forex/main.go @@ -1,4 +1,4 @@ -// Copyright 2025 Raphael Thomazella. All rights reserved. +// Copyright 2025-present R. Thomazella. All rights reserved. // Use of this source code is governed by the BSD-3-Clause // license that can be found in the LICENSE file and online // at https://opensource.org/license/BSD-3-clause. From f1211b60bcbbdc835d415aea422dad1b7b0c02c7 Mon Sep 17 00:00:00 2001 From: "Raphael \"Thom\" Thomazella" Date: Fri, 27 Mar 2026 21:58:55 -0300 Subject: [PATCH 10/23] misc(.gitconfig): update my name --- dotfiles/.config/jj/config.toml | 2 +- dotfiles/.gitconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotfiles/.config/jj/config.toml b/dotfiles/.config/jj/config.toml index 3dc70e6..1376044 100644 --- a/dotfiles/.config/jj/config.toml +++ b/dotfiles/.config/jj/config.toml @@ -1,7 +1,7 @@ "$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json" [user] -name = "Raphael \"Thom\" Thomazella" +name = "R. Thomazella" email = "thomazella9@gmail.com" [signing] diff --git a/dotfiles/.gitconfig b/dotfiles/.gitconfig index 7f9185a..fc036ff 100644 --- a/dotfiles/.gitconfig +++ b/dotfiles/.gitconfig @@ -1,5 +1,5 @@ [user] - name = Raphael \"Thom\" Thomazella + name = R. Thomazella email = thomazella9@gmail.com signingkey = D600E88A0C5FE062 [core] From 7b7139fced168ea6b788ceca8bd75e81c7c88550 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Fri, 27 Mar 2026 22:10:02 -0300 Subject: [PATCH 11/23] feat(.config/code-oss): go pls options --- .../.config/Claude/claude_desktop_config.json | 7 +- dotfiles/.config/code-oss/User/settings.json | 116 ++++++++++++------ 2 files changed, 84 insertions(+), 39 deletions(-) diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json index 433cc09..c002554 100644 --- a/dotfiles/.config/Claude/claude_desktop_config.json +++ b/dotfiles/.config/Claude/claude_desktop_config.json @@ -1,13 +1,12 @@ { "mcpServers": { - "jailMPC": { + "__jailMCP": { "command": "docker", "args": [ "compose", "-f", "/home/vacation/Desktop/jail-mcp/docker-compose.yml", "run", - "--rm", "-i", "jail-mcp" ] @@ -17,8 +16,8 @@ "preferences": { "quickEntryShortcut": "off", "quickEntryDictationShortcut": "capslock", - "coworkScheduledTasksEnabled": false, - "ccdScheduledTasksEnabled": false, + "coworkScheduledTasksEnabled": true, + "ccdScheduledTasksEnabled": true, "sidebarMode": "chat", "coworkWebSearchEnabled": true } diff --git a/dotfiles/.config/code-oss/User/settings.json b/dotfiles/.config/code-oss/User/settings.json index 34f4f80..6457137 100644 --- a/dotfiles/.config/code-oss/User/settings.json +++ b/dotfiles/.config/code-oss/User/settings.json @@ -18,7 +18,7 @@ "tab.activeBackground": "#000", "titleBar.activeBackground": "#000", "editorGutter.background": "#000", - "breadcrumb.background": "#000" + "breadcrumb.background": "#000", }, "activityBarBadge.background": "#FF4081", "list.activeSelectionForeground": "#FF4081", @@ -38,7 +38,7 @@ "panelTitle.activeBorder": "#FF4081", "breadcrumb.activeSelectionForeground": "#FF4081", "menu.selectionForeground": "#FF4081", - "menubar.selectionForeground": "#FF4081" + "menubar.selectionForeground": "#FF4081", }, "editor.tokenColorCustomizations": { "[GitHub Dark High Contrast]": { @@ -46,8 +46,8 @@ "functions": "#7e7fdd", "strings": "#8b907f", "keywords": "#c5ad97", - "types": "#6abe7f" - } + "types": "#6abe7f", + }, }, // // - - - - - - - - FONT - - - - - - - - - @@ -68,31 +68,31 @@ // "eslint.run": "onType", "eslint.options": { - "resolvePluginsRelativeTo": "." + "resolvePluginsRelativeTo": ".", }, "[shellscript]": { - "editor.defaultFormatter": "lumirelle.shell-format-rev" + "editor.defaultFormatter": "lumirelle.shell-format-rev", }, "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[sql]": { - "editor.formatOnPaste": false + "editor.formatOnPaste": false, }, "[yaml]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "sql-formatter.uppercase": true, // @@ -120,10 +120,10 @@ "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.importModuleSpecifierEnding": "minimal", "[html]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode", }, "typescript.implementationsCodeLens.enabled": true, "typescript.updateImportsOnFileMove.enabled": "always", @@ -134,22 +134,68 @@ "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.organizeImports": "explicit" - } + "source.organizeImports": "explicit", + }, }, "go.testOnSave": false, "go.delveConfig": { - "debugAdapter": "dlv-dap" + "debugAdapter": "dlv-dap", }, "go.testEnvFile": "${workspaceFolder}/.env", "go.testFlags": ["-race"], "go.lintTool": "golangci-lint", "go.lintFlags": ["--print-issued-lines=false"], "go.vetOnSave": "off", - "go.formatTool": "gofumpt", + "go.formatTool": "default", "gopls": { "ui.semanticTokens": true, - "build.buildFlags": ["-buildvcs=false"] + "build.buildFlags": ["-buildvcs=false"], + + // Show inlay hints for parameter names, type params, etc. + "ui.inlayhints.parameterNames": true, + "ui.inlayhints.assignVariableTypes": true, + "ui.inlayhints.compositeLiteralFields": true, + "ui.inlayhints.compositeLiteralTypes": true, + "ui.inlayhints.constantValues": true, + "ui.inlayhints.functionTypeParameters": true, + "ui.inlayhints.rangeVariableTypes": true, + + // Staticcheck: enables many extra linters beyond go vet + "ui.diagnostic.staticcheck": true, + + // Analyses: fine-grained control over individual analyzers + "ui.diagnostic.analyses": { + "shadow": true, // warn on shadowed variables + "nilness": true, // detect nil dereferences + "unusedparams": true, // warn on unused function params + "unusedwrite": true, // warn on writes to vars never read + "useany": true, // suggest `any` instead of `interface{}` + }, + + // Completion options + "ui.completion.usePlaceholders": true, // fill in param placeholders on completion + "ui.completion.matcher": "Fuzzy", // fuzzy matching (vs "CaseInsensitive" or "CaseSensitive") + + // Codelens: inline actionable buttons + "ui.codelenses": { + "gc_details": true, // toggle compiler optimization details + "run_govulncheck": true, + "tidy": true, + "vendor": true, + "generate": true, + }, + + // Format with gofumpt (stricter than gofmt) — requires gofumpt installed + "formatting.gofumpt": true, + + // Automatically add missing imports and remove unused ones on save + // (usually controlled by editor settings, but can be forced here) + "ui.diagnostic.annotations": { + "bounds": true, // annotate slice bounds checks + "escape": true, // annotate heap escapes + "inline": true, // annotate inlining decisions + "nil": true, + }, }, "go.toolsManagement.autoUpdate": true, // @@ -193,28 +239,28 @@ "githubPullRequests.queries": [ { "label": "Local Pull Request Branches", - "query": "default" + "query": "default", }, { "label": "Waiting", - "query": "repo:${owner}/${repository} is:open review-requested:${user}" + "query": "repo:${owner}/${repository} is:open review-requested:${user}", }, { "label": "Open", - "query": "repo:${owner}/${repository} is:open" + "query": "repo:${owner}/${repository} is:open", }, { "label": "Assigned", - "query": "repo:${owner}/${repository} is:open assignee:${user}" + "query": "repo:${owner}/${repository} is:open assignee:${user}", }, { "label": "Created", - "query": "repo:${owner}/${repository} is:open author:${user}" + "query": "repo:${owner}/${repository} is:open author:${user}", }, { "label": "All Open", - "query": "default" - } + "query": "default", + }, ], "githubPullRequests.fileListLayout": "flat", "githubPullRequests.pullBranch": "never", @@ -222,7 +268,7 @@ "*": true, "plaintext": false, "markdown": false, - "scminput": false + "scminput": false, }, "cSpell.userWords": [ "AIAPI", @@ -293,7 +339,7 @@ "Unnotified", "vamac", "Wrapf", - "zerolog" + "zerolog", ], "cSpell.maxDuplicateProblems": 2, "cSpell.spellCheckDelayMs": 2000, @@ -308,10 +354,10 @@ "css", "javascript", "typescript", - "shellscript" + "shellscript", ], "cSpell.enabledNotifications": { - "Lines too Long": false + "Lines too Long": false, }, "window.commandCenter": false, "window.menuBarVisibility": "hidden", @@ -328,15 +374,15 @@ "editor.insertSpaces": true, "editor.tabSize": 2, "editor.autoIndent": "advanced", - "editor.defaultFormatter": "redhat.vscode-yaml" + "editor.defaultFormatter": "redhat.vscode-yaml", }, "[github-actions-workflow]": { - "editor.defaultFormatter": "redhat.vscode-yaml" + "editor.defaultFormatter": "redhat.vscode-yaml", }, "github.copilot.nextEditSuggestions.enabled": true, "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - } + "editor.defaultFormatter": "esbenp.prettier-vscode", + }, // // - - - - - - - - NEW - - - - - - - - - // From 7a16391d5570a0b78283c9f43dbe23efed972d58 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sat, 28 Mar 2026 23:16:57 -0300 Subject: [PATCH 12/23] misc(claude): update config --- dotfiles/.config/Claude/claude_desktop_config.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json index c002554..b55b353 100644 --- a/dotfiles/.config/Claude/claude_desktop_config.json +++ b/dotfiles/.config/Claude/claude_desktop_config.json @@ -1,12 +1,13 @@ { "mcpServers": { - "__jailMCP": { + "3_jailMCP": { "command": "docker", "args": [ "compose", "-f", "/home/vacation/Desktop/jail-mcp/docker-compose.yml", "run", + "--rm", "-i", "jail-mcp" ] @@ -16,8 +17,8 @@ "preferences": { "quickEntryShortcut": "off", "quickEntryDictationShortcut": "capslock", - "coworkScheduledTasksEnabled": true, - "ccdScheduledTasksEnabled": true, + "coworkScheduledTasksEnabled": false, + "ccdScheduledTasksEnabled": false, "sidebarMode": "chat", "coworkWebSearchEnabled": true } From 2c1fec589141bc315113282be8c147759296f793 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 00:52:00 -0300 Subject: [PATCH 13/23] refactor(dictation-shortcut): use whisper locally --- bin/dictation-keyboard-hook | 41 +++++++++++++++---- .../systemd/user/dictation-shortcut.service | 2 +- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 57f0898..ecdcc5f 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -import sys, evdev, subprocess, time +"""Push-to-talk dictation: record while F2 held, transcribe on release.""" +import sys, subprocess, tempfile, os from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -11,13 +12,35 @@ if len(sys.argv) != 3: key = sys.argv[1] device = InputDevice(sys.argv[2]) -subprocess.Popen(['/usr/bin/nerd-dictation', 'begin', '--numbers-as-digits', "--suspend-on-start", "--simulate-input-tool=YDOTOOL", "--idle-time=0", "--verbose=1"]) +MODEL = "/usr/share/whisper.cpp-model-large-v3-turbo-q5_0/ggml-large-v3-turbo-q5_0.bin" + +recording = None +wav = None for event in device.read_loop(): - if event.type == ecodes.EV_KEY: - k = categorize(event) - if k.keycode == key: - if k.keystate == k.key_down: - subprocess.Popen(['/usr/bin/nerd-dictation', 'resume']) - elif k.keystate == k.key_up: - subprocess.Popen(['/usr/bin/nerd-dictation', 'suspend']) + if event.type != ecodes.EV_KEY: + continue + k = categorize(event) + if k.keycode != key: + continue + + if k.keystate == k.key_down and recording is None: + wav = tempfile.mktemp(suffix=".wav") + recording = subprocess.Popen(["pw-record", "--rate=16000", "--channels=1", wav]) + + elif k.keystate == k.key_up and recording is not None: + recording.terminate() + recording.wait() + recording = None + + result = subprocess.run( + ["whisper-cli", "-m", MODEL, "-f", wav, + "--no-timestamps", "--language", "auto", "--suppress-nst"], + capture_output=True, text=True + ) + print(result.stderr, end="", flush=True) # whisper logs visible in journal + text = result.stdout.strip() + if text: + subprocess.run(["ydotool", "type", "--key-delay=5", "--key-hold=0", "--", text + " "]) + os.unlink(wav) + wav = None diff --git a/dotfiles/.config/systemd/user/dictation-shortcut.service b/dotfiles/.config/systemd/user/dictation-shortcut.service index c23774f..4be1789 100644 --- a/dotfiles/.config/systemd/user/dictation-shortcut.service +++ b/dotfiles/.config/systemd/user/dictation-shortcut.service @@ -2,7 +2,7 @@ Description=Dictation shortcut on key down and key up [Service] -ExecStart=/usr/bin/python3 %h/bin/dictation-keyboard-hook KEY_F2 /dev/input/by-id/usb-Keychron_Keychron_Q10-if02-event-kbd +ExecStart=%h/bin/dictation-keyboard-hook KEY_F2 /dev/input/by-id/usb-Keychron_Keychron_Q10-if02-event-kbd Restart=on-failure RestartSec=5 StartLimitBurst=3 From 7e70ceaf58d61027e182e021af140d4dd10899de Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 02:01:15 -0300 Subject: [PATCH 14/23] misc(bin/dictation-keyboard-hook): use threading --- bin/dictation-keyboard-hook | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index ecdcc5f..9f9eeea 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """Push-to-talk dictation: record while F2 held, transcribe on release.""" -import sys, subprocess, tempfile, os +import sys, subprocess, tempfile, os, threading from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -17,6 +17,19 @@ MODEL = "/usr/share/whisper.cpp-model-large-v3-turbo-q5_0/ggml-large-v3-turbo-q5 recording = None wav = None +def transcribe(wav): + result = subprocess.run( + ["whisper-cli", "-m", MODEL, "-f", wav, + "--no-timestamps", "--language", "auto", "--suppress-nst"], + capture_output=True, text=True + ) + print(result.stderr, end="", flush=True) + print(result.stdout, end="", flush=True) + text = result.stdout.strip() + if text: + subprocess.run(["ydotool", "type", "--key-delay=5", "--key-hold=0", "--", text + " "]) + os.unlink(wav) + for event in device.read_loop(): if event.type != ecodes.EV_KEY: continue @@ -32,15 +45,5 @@ for event in device.read_loop(): recording.terminate() recording.wait() recording = None - - result = subprocess.run( - ["whisper-cli", "-m", MODEL, "-f", wav, - "--no-timestamps", "--language", "auto", "--suppress-nst"], - capture_output=True, text=True - ) - print(result.stderr, end="", flush=True) # whisper logs visible in journal - text = result.stdout.strip() - if text: - subprocess.run(["ydotool", "type", "--key-delay=5", "--key-hold=0", "--", text + " "]) - os.unlink(wav) + threading.Thread(target=transcribe, args=(wav,), daemon=True).start() wav = None From 654ffee0ba7e71f67658f1dcbfa959122a5af347 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 02:17:20 -0300 Subject: [PATCH 15/23] refactor(systemd/dictation-shortcut): use whisper as backend --- bin/dictation-keyboard-hook | 19 ++++--- doc/bin.md | 50 +++++++++---------- doc/workflows.md | 25 +++++----- .../whisper-server.service | 1 + .../systemd/user/dictation-shortcut.service | 2 + .../systemd/user/whisper-server.service | 13 +++++ 6 files changed, 66 insertions(+), 44 deletions(-) create mode 120000 dotfiles/.config/systemd/user/default.target.wants/whisper-server.service create mode 100644 dotfiles/.config/systemd/user/whisper-server.service diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 9f9eeea..7ce0eac 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -"""Push-to-talk dictation: record while F2 held, transcribe on release.""" -import sys, subprocess, tempfile, os, threading +"""Push-to-talk dictation: record while F2 held, transcribe via whisper-server on release.""" +import sys, subprocess, tempfile, os, threading, json, signal from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -12,20 +12,25 @@ if len(sys.argv) != 3: key = sys.argv[1] device = InputDevice(sys.argv[2]) -MODEL = "/usr/share/whisper.cpp-model-large-v3-turbo-q5_0/ggml-large-v3-turbo-q5_0.bin" +SERVER = "http://127.0.0.1:8765/inference" recording = None wav = None def transcribe(wav): result = subprocess.run( - ["whisper-cli", "-m", MODEL, "-f", wav, - "--no-timestamps", "--language", "auto", "--suppress-nst"], + ["curl", "-sf", + "-F", f"file=@{wav}", + SERVER], capture_output=True, text=True ) print(result.stderr, end="", flush=True) print(result.stdout, end="", flush=True) - text = result.stdout.strip() + try: + text = json.loads(result.stdout).get("text", "").strip() + except json.JSONDecodeError: + print(f"whisper-server parse error: {result.stdout}", flush=True) + text = "" if text: subprocess.run(["ydotool", "type", "--key-delay=5", "--key-hold=0", "--", text + " "]) os.unlink(wav) @@ -42,7 +47,7 @@ for event in device.read_loop(): recording = subprocess.Popen(["pw-record", "--rate=16000", "--channels=1", wav]) elif k.keystate == k.key_up and recording is not None: - recording.terminate() + recording.send_signal(signal.SIGINT) recording.wait() recording = None threading.Thread(target=transcribe, args=(wav,), daemon=True).start() diff --git a/doc/bin.md b/doc/bin.md index 9141730..7430c76 100644 --- a/doc/bin.md +++ b/doc/bin.md @@ -5,31 +5,31 @@ Scripts use the standard header: `set -euo pipefail`, `trap 'err $LINENO' ERR`. ## Shell Scripts -| Script | Purpose | -| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `snapshot` | Interactive btrfs subvolume snapshot tool. Prompts for source subvolume under `/toplevel`, name, and read-only flag. Supports `--dry-run`. | -| `system-snapshot` | Non-interactive btrfs snapshot for automated use. Takes target subvolume names as args, keeps max 50 snapshots, trims old ones. | -| `system-up` | Full Arch Linux system update: snapshot → stop postgres → update pacman/AUR/mise globals → migrate postgres if major version changed → btrfs scrub optional → kexec into new kernel. | -| `btrfs-scrub` | Runs `btrfs scrub` on specified disks. | -| `app-inhibit-sleep` | Watches if Firefox (or given apps) is playing audio via `playerctl`; calls `systemd-inhibit` to prevent idle sleep while playing. | -| `lazy-git` | Interactive git helper: stages, composits commitlint-compliant commit messages, and pushes repos listed in `$PUSH_REPOS`. Reads `$GIT_BRANCH`. | -| `lazy-jujutsu` | Same as `lazy-git` but for jujutsu (`jj`). | -| `extract` | Universal archive extractor. Detects format and calls the right tool (tar, unzip, 7z, etc.). | -| `hue` | Philips Hue light controller (~425 lines). Talks to the Hue bridge REST API. | -| `compile-bin` | Builds all Go tools under `src/` into `bin/`. Iterates `src/**/main.go`, runs `go build -race`. | -| `bulk-curl-from-file` | Runs curl for each URL in a file. | -| `cpfgen` | CPF number generator (Brazilian tax ID). | -| `dictation-keyboard-hook` | Keyboard hook for dictation shortcut. | -| `dir-rename` | Compiled Go binary — bulk directory rename tool. | -| `download-youtube-id` | Downloads a YouTube video by ID using `yt-dlp`. | -| `forex` | Compiled Go binary — currency exchange rate fetcher. | -| `game-audio-fix` | Fixes audio for games (likely resets PulseAudio/PipeWire). | -| `mac-defaults` | Applies `defaults write` settings for macOS. | -| `presentvalue` | Compiled Go binary — present value financial calculator. | -| `rename` | Perl script for powerful batch file renaming (regex-based). | -| `system-up` | (see above) | -| `template` | Scaffold script — creates new scripts from a template. | -| `typo` | Compiled binary — likely a typo/spell checker helper. | +| Script | Purpose | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `snapshot` | Interactive btrfs subvolume snapshot tool. Prompts for source subvolume under `/toplevel`, name, and read-only flag. Supports `--dry-run`. | +| `system-snapshot` | Non-interactive btrfs snapshot for automated use. Takes target subvolume names as args, keeps max 50 snapshots, trims old ones. | +| `system-up` | Full Arch Linux system update: snapshot → stop postgres → update pacman/AUR/mise globals → migrate postgres if major version changed → btrfs scrub optional → kexec into new kernel. | +| `btrfs-scrub` | Runs `btrfs scrub` on specified disks. | +| `app-inhibit-sleep` | Watches if Firefox (or given apps) is playing audio via `playerctl`; calls `systemd-inhibit` to prevent idle sleep while playing. | +| `lazy-git` | Interactive git helper: stages, composits commitlint-compliant commit messages, and pushes repos listed in `$PUSH_REPOS`. Reads `$GIT_BRANCH`. | +| `lazy-jujutsu` | Same as `lazy-git` but for jujutsu (`jj`). | +| `extract` | Universal archive extractor. Detects format and calls the right tool (tar, unzip, 7z, etc.). | +| `hue` | Philips Hue light controller (~425 lines). Talks to the Hue bridge REST API. | +| `compile-bin` | Builds all Go tools under `src/` into `bin/`. Iterates `src/**/main.go`, runs `go build -race`. | +| `bulk-curl-from-file` | Runs curl for each URL in a file. | +| `cpfgen` | CPF number generator (Brazilian tax ID). | +| `dictation-keyboard-hook` | Push-to-talk dictation via whisper-server. Records with `pw-record` while `KEY_F2` held, POSTs WAV to `whisper-server` on release, types result via `ydotool`. Multilingual (auto-detect). | +| `dir-rename` | Compiled Go binary — bulk directory rename tool. | +| `download-youtube-id` | Downloads a YouTube video by ID using `yt-dlp`. | +| `forex` | Compiled Go binary — currency exchange rate fetcher. | +| `game-audio-fix` | Fixes audio for games (likely resets PulseAudio/PipeWire). | +| `mac-defaults` | Applies `defaults write` settings for macOS. | +| `presentvalue` | Compiled Go binary — present value financial calculator. | +| `rename` | Perl script for powerful batch file renaming (regex-based). | +| `system-up` | (see above) | +| `template` | Scaffold script — creates new scripts from a template. | +| `typo` | Compiled binary — likely a typo/spell checker helper. | ## Go Sources (`src/`) diff --git a/doc/workflows.md b/doc/workflows.md index 1352339..b1f5a23 100644 --- a/doc/workflows.md +++ b/doc/workflows.md @@ -92,18 +92,19 @@ Maps volume labels to mount points and runs `btrfs scrub start` (or `cancel`). ## User systemd services (dotfiles/.config/systemd/user/) -| Service | Timer | Purpose | -| ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------- | -| `app-inhibit-sleep` | always-on | Watches `pacman`, `yay`, and Firefox audio via `playerctl`; calls `systemd-inhibit` to block idle sleep | -| `forex@.service` | hourly | Fetches exchange rate for currency `%i`; reads env from `~/.config/t0/forex.env` | -| `game-audio-fix` | every minute | Corrects 5.1→2.0 audio routing for games | -| `docker-prune` | Saturdays 15–23h | `docker system prune` | -| `system-update-notification` | Sundays 11:00 | `notify-send` reminder to run system update | -| `ollama` | always-on | `ollama serve` with ROCm (AMD GPU), listens on `0.0.0.0:11434` for Docker bridge access | -| `xkbcomp` | on graphical session | Loads custom XKB keymap from `dotfiles/.config/keymap.xkb` | -| `xset-rate` | on default target | Sets key repeat: 140ms delay, 70ms interval | -| `dictation-shortcut` | always-on | Python keyboard hook on `KEY_F2` (Keychron Q10) for dictation | -| `docker` override | — | Sets `DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=false` so containers can reach host | +| Service | Timer | Purpose | +| ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `app-inhibit-sleep` | always-on | Watches `pacman`, `yay`, and Firefox audio via `playerctl`; calls `systemd-inhibit` to block idle sleep | +| `forex@.service` | hourly | Fetches exchange rate for currency `%i`; reads env from `~/.config/t0/forex.env` | +| `game-audio-fix` | every minute | Corrects 5.1→2.0 audio routing for games | +| `docker-prune` | Saturdays 15–23h | `docker system prune` | +| `system-update-notification` | Sundays 11:00 | `notify-send` reminder to run system update | +| `ollama` | always-on | `ollama serve` with ROCm (AMD GPU), listens on `0.0.0.0:11434` for Docker bridge access | +| `xkbcomp` | on graphical session | Loads custom XKB keymap from `dotfiles/.config/keymap.xkb` | +| `xset-rate` | on default target | Sets key repeat: 140ms delay, 70ms interval | +| `whisper-server` | always-on | `whisper-server` with model loaded on GPU (ROCm); listens on `127.0.0.1:8765` for dictation inference | +| `dictation-shortcut` | always-on | Push-to-talk dictation on `KEY_F2` (Keychron Q10); records via `pw-record`, transcribes via `whisper-server`, types via `ydotool`. Requires `whisper-server`. | +| `docker` override | — | Sets `DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=false` so containers can reach host | ## Prompt diff --git a/dotfiles/.config/systemd/user/default.target.wants/whisper-server.service b/dotfiles/.config/systemd/user/default.target.wants/whisper-server.service new file mode 120000 index 0000000..4bcb4ad --- /dev/null +++ b/dotfiles/.config/systemd/user/default.target.wants/whisper-server.service @@ -0,0 +1 @@ +/home/vacation/.config/systemd/user/whisper-server.service \ No newline at end of file diff --git a/dotfiles/.config/systemd/user/dictation-shortcut.service b/dotfiles/.config/systemd/user/dictation-shortcut.service index 4be1789..4bf66ed 100644 --- a/dotfiles/.config/systemd/user/dictation-shortcut.service +++ b/dotfiles/.config/systemd/user/dictation-shortcut.service @@ -1,5 +1,7 @@ [Unit] Description=Dictation shortcut on key down and key up +After=whisper-server.service +Wants=whisper-server.service [Service] ExecStart=%h/bin/dictation-keyboard-hook KEY_F2 /dev/input/by-id/usb-Keychron_Keychron_Q10-if02-event-kbd diff --git a/dotfiles/.config/systemd/user/whisper-server.service b/dotfiles/.config/systemd/user/whisper-server.service new file mode 100644 index 0000000..c3b53cd --- /dev/null +++ b/dotfiles/.config/systemd/user/whisper-server.service @@ -0,0 +1,13 @@ +[Unit] +Description=Whisper.cpp inference server + +[Service] +ExecStart=/usr/bin/whisper-server \ + -m /usr/share/whisper.cpp-model-large-v3-turbo-q5_0/ggml-large-v3-turbo-q5_0.bin \ + --host 127.0.0.1 --port 8765 \ + --language auto --suppress-nst --no-timestamps --convert +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=default.target From 6e6117892f0be3be446f9b0ae355fb5b48a81d36 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 11:24:17 -0300 Subject: [PATCH 16/23] refactor(bin/dictation-keyboard-hook): use xdotool to support latin characters --- bin/dictation-keyboard-hook | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 7ce0eac..2de77c3 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """Push-to-talk dictation: record while F2 held, transcribe via whisper-server on release.""" -import sys, subprocess, tempfile, os, threading, json, signal +import sys, subprocess, tempfile, os, threading, json, signal, glob from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -14,6 +14,25 @@ device = InputDevice(sys.argv[2]) SERVER = "http://127.0.0.1:8765/inference" +_x_env = {} + +def get_x_env(): + """Discover DISPLAY and XAUTHORITY from the running user session, cached. xdotool dependency""" + global _x_env + if _x_env: + return _x_env + for pid_env in glob.glob("/proc/*/environ"): + try: + with open(pid_env, "rb") as f: + pairs = f.read().split(b"\x00") + d = {k.decode(): v.decode() for p in pairs if b"=" in p for k, _, v in [p.partition(b"=")]} + if "DISPLAY" in d and "XAUTHORITY" in d: + _x_env = {"DISPLAY": d["DISPLAY"], "XAUTHORITY": d["XAUTHORITY"]} + return _x_env + except (PermissionError, FileNotFoundError): + continue + return {} + recording = None wav = None @@ -32,7 +51,8 @@ def transcribe(wav): print(f"whisper-server parse error: {result.stdout}", flush=True) text = "" if text: - subprocess.run(["ydotool", "type", "--key-delay=5", "--key-hold=0", "--", text + " "]) + env = {**os.environ, **get_x_env()} + subprocess.run(["xdotool", "type", "--clearmodifiers", "--delay", "3", "--", text + " "], env=env) os.unlink(wav) for event in device.read_loop(): From c4e8d124a795b4710d77c717c717ec8ee1565bc0 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 11:50:00 -0300 Subject: [PATCH 17/23] fix(bin/dictation-keyboard-hook): weird characters --- bin/dictation-keyboard-hook | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 2de77c3..2b960d8 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """Push-to-talk dictation: record while F2 held, transcribe via whisper-server on release.""" -import sys, subprocess, tempfile, os, threading, json, signal, glob +import sys, subprocess, tempfile, os, json, signal, glob from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -13,11 +13,10 @@ key = sys.argv[1] device = InputDevice(sys.argv[2]) SERVER = "http://127.0.0.1:8765/inference" - _x_env = {} def get_x_env(): - """Discover DISPLAY and XAUTHORITY from the running user session, cached. xdotool dependency""" + """Discover DISPLAY and XAUTHORITY from the running user session, cached.""" global _x_env if _x_env: return _x_env @@ -34,26 +33,6 @@ def get_x_env(): return {} recording = None -wav = None - -def transcribe(wav): - result = subprocess.run( - ["curl", "-sf", - "-F", f"file=@{wav}", - SERVER], - capture_output=True, text=True - ) - print(result.stderr, end="", flush=True) - print(result.stdout, end="", flush=True) - try: - text = json.loads(result.stdout).get("text", "").strip() - except json.JSONDecodeError: - print(f"whisper-server parse error: {result.stdout}", flush=True) - text = "" - if text: - env = {**os.environ, **get_x_env()} - subprocess.run(["xdotool", "type", "--clearmodifiers", "--delay", "3", "--", text + " "], env=env) - os.unlink(wav) for event in device.read_loop(): if event.type != ecodes.EV_KEY: @@ -70,5 +49,25 @@ for event in device.read_loop(): recording.send_signal(signal.SIGINT) recording.wait() recording = None - threading.Thread(target=transcribe, args=(wav,), daemon=True).start() + + result = subprocess.run( + ["curl", "-sf", "-F", f"file=@{wav}", SERVER], + capture_output=True, text=True + ) + print(result.stderr, end="", flush=True) + print(result.stdout, end="", flush=True) + try: + text = json.loads(result.stdout).get("text", "").strip() + print("\n|"+text+"|", flush=True) + except json.JSONDecodeError: + print(f"whisper-server parse error: {result.stdout}", flush=True) + text = "" + if text: + env = {**os.environ, **get_x_env()} + # save and restore clipboard to avoid clobbering it + prev = subprocess.run(["xclip", "-selection", "clipboard", "-o"], capture_output=True, env=env).stdout + subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode(), env=env) + subprocess.run(["xdotool", "key", "ctrl+v"], env=env) + subprocess.run(["xclip", "-selection", "clipboard"], input=prev, env=env) + os.unlink(wav) wav = None From e8d0e24aa39cd3048a182d1272887809bed042c8 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 12:17:55 -0300 Subject: [PATCH 18/23] refactor(bin/dictation-keyboard-hook): y do tool paste --- bin/dictation-keyboard-hook | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 2b960d8..e55b1b9 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """Push-to-talk dictation: record while F2 held, transcribe via whisper-server on release.""" -import sys, subprocess, tempfile, os, json, signal, glob +import sys, subprocess, tempfile, os, json, signal from evdev import InputDevice, categorize, ecodes if len(sys.argv) != 3: @@ -13,25 +13,6 @@ key = sys.argv[1] device = InputDevice(sys.argv[2]) SERVER = "http://127.0.0.1:8765/inference" -_x_env = {} - -def get_x_env(): - """Discover DISPLAY and XAUTHORITY from the running user session, cached.""" - global _x_env - if _x_env: - return _x_env - for pid_env in glob.glob("/proc/*/environ"): - try: - with open(pid_env, "rb") as f: - pairs = f.read().split(b"\x00") - d = {k.decode(): v.decode() for p in pairs if b"=" in p for k, _, v in [p.partition(b"=")]} - if "DISPLAY" in d and "XAUTHORITY" in d: - _x_env = {"DISPLAY": d["DISPLAY"], "XAUTHORITY": d["XAUTHORITY"]} - return _x_env - except (PermissionError, FileNotFoundError): - continue - return {} - recording = None for event in device.read_loop(): @@ -63,11 +44,11 @@ for event in device.read_loop(): print(f"whisper-server parse error: {result.stdout}", flush=True) text = "" if text: - env = {**os.environ, **get_x_env()} # save and restore clipboard to avoid clobbering it - prev = subprocess.run(["xclip", "-selection", "clipboard", "-o"], capture_output=True, env=env).stdout - subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode(), env=env) - subprocess.run(["xdotool", "key", "ctrl+v"], env=env) - subprocess.run(["xclip", "-selection", "clipboard"], input=prev, env=env) + prev = subprocess.run(["xclip", "-selection", "clipboard", "-o"], capture_output=True).stdout + subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode()) + # ctrl+v keycodes + subprocess.run(["ydotool", "key", "97:1", "47:1", "97:0", "47:0"]) + subprocess.run(["xclip", "-selection", "clipboard"], input=prev) os.unlink(wav) wav = None From 9051a56707c7e9694e6a89aec9e7a76d4f3dcfaa Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 12:28:17 -0300 Subject: [PATCH 19/23] refactor(bin/dictation-keyboard-hook): wl-copy --- bin/dictation-keyboard-hook | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index e55b1b9..6a5320b 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -39,16 +39,11 @@ for event in device.read_loop(): print(result.stdout, end="", flush=True) try: text = json.loads(result.stdout).get("text", "").strip() - print("\n|"+text+"|", flush=True) except json.JSONDecodeError: print(f"whisper-server parse error: {result.stdout}", flush=True) text = "" if text: - # save and restore clipboard to avoid clobbering it - prev = subprocess.run(["xclip", "-selection", "clipboard", "-o"], capture_output=True).stdout - subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode()) - # ctrl+v keycodes - subprocess.run(["ydotool", "key", "97:1", "47:1", "97:0", "47:0"]) - subprocess.run(["xclip", "-selection", "clipboard"], input=prev) + subprocess.run(["wl-copy", text + " "]) + subprocess.run(["ydotool", "key", "29:1", "47:1", "47:0", "29:0"]) os.unlink(wav) wav = None From d5afe69fab80014c35e76fc9bc1a14ac030f28a4 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 13:31:42 -0300 Subject: [PATCH 20/23] misc: code review --- AGENTS.md | 2 +- bin/dictation-keyboard-hook | 1 + bin/setup | 2 +- dotfiles/.config/systemd/user/whisper-server.service | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62b099c..0435e0d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ unalias foo 2>/dev/null foo() { ``` this fixes weird bash errors when an alias and a function conflict. -separate functions neatly using #---------------- +separate functions neatly using #----------------# ## Misc diff --git a/bin/dictation-keyboard-hook b/bin/dictation-keyboard-hook index 6a5320b..bc85a03 100755 --- a/bin/dictation-keyboard-hook +++ b/bin/dictation-keyboard-hook @@ -44,6 +44,7 @@ for event in device.read_loop(): text = "" if text: subprocess.run(["wl-copy", text + " "]) + # ctrl+v keycodes, 1 is keydown subprocess.run(["ydotool", "key", "29:1", "47:1", "47:0", "29:0"]) os.unlink(wav) wav = None diff --git a/bin/setup b/bin/setup index 8df1cc8..04e5d22 100755 --- a/bin/setup +++ b/bin/setup @@ -24,7 +24,7 @@ fatal() { trap 'err $LINENO' ERR -readonly install_dir="/usr/local/bin" +readonly install_dir="/root/bin" run_privileged() { if [ "$(id -u)" = "0" ]; then diff --git a/dotfiles/.config/systemd/user/whisper-server.service b/dotfiles/.config/systemd/user/whisper-server.service index c3b53cd..a60fa3a 100644 --- a/dotfiles/.config/systemd/user/whisper-server.service +++ b/dotfiles/.config/systemd/user/whisper-server.service @@ -1,5 +1,6 @@ [Unit] Description=Whisper.cpp inference server +# note: ffmpeg is a dependency [Service] ExecStart=/usr/bin/whisper-server \ From 9bddb523a50648e18216019c1a09b00dbe93b211 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 13:32:21 -0300 Subject: [PATCH 21/23] misc: lintfix --- dotfiles/lib-git-prompt.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotfiles/lib-git-prompt.sh b/dotfiles/lib-git-prompt.sh index a2e79c7..8ce1592 100644 --- a/dotfiles/lib-git-prompt.sh +++ b/dotfiles/lib-git-prompt.sh @@ -124,7 +124,7 @@ __git_ps1_show_upstream() { fi ;; svn-remote.*.url) - svn_remote[${#svn_remote[@]}+1]="$value" + svn_remote[${#svn_remote[@]} + 1]="$value" svn_url_pattern="$svn_url_pattern\\|$value" upstream=svn+git # default upstream is SVN if available, else git ;; @@ -151,7 +151,7 @@ __git_ps1_show_upstream() { mapfile -t svn_upstream < <(git log --first-parent -1 \ --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null) if [[ 0 -ne ${#svn_upstream[@]} ]]; then - local svn_upstream_str=${svn_upstream[${#svn_upstream[@]}-2]} + local svn_upstream_str=${svn_upstream[${#svn_upstream[@]} - 2]} svn_upstream_str=${svn_upstream_str%@*} local n_stop="${#svn_remote[@]}" for ((n = 1; n <= n_stop; n++)); do From 4d1a8bd7cef7138af4e25a6fa1de89e76be6a6a1 Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 13:35:18 -0300 Subject: [PATCH 22/23] misc: fix setup for CI --- .github/workflows/shellcheck.yml | 2 +- .github/workflows/shfmt.yml | 2 +- bin/setup | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 0dc8284..06161d7 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - name: install tools - run: bin/setup + run: bin/setup /usr/local/bin - name: run shellcheck run: | diff --git a/.github/workflows/shfmt.yml b/.github/workflows/shfmt.yml index 986bb43..5e12c49 100644 --- a/.github/workflows/shfmt.yml +++ b/.github/workflows/shfmt.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - name: install tools - run: bin/setup + run: bin/setup /usr/local/bin - name: run shfmt run: | diff --git a/bin/setup b/bin/setup index 04e5d22..9fec3a0 100755 --- a/bin/setup +++ b/bin/setup @@ -24,7 +24,7 @@ fatal() { trap 'err $LINENO' ERR -readonly install_dir="/root/bin" +readonly install_dir="${1:-/root/bin}" run_privileged() { if [ "$(id -u)" = "0" ]; then @@ -112,6 +112,7 @@ if ! $missing; then exit 0 fi +# todo: runing script as user doesn't trigger this # interactive shell: just tell the user if [[ $- == *i* ]]; then print_manual_instructions From c68f29aeea0340a5ab9b8a61ec7d6a6a2f92aaff Mon Sep 17 00:00:00 2001 From: "R. Thomazella" Date: Sun, 29 Mar 2026 13:44:51 -0300 Subject: [PATCH 23/23] misc: format --- dotfiles/lib-git-prompt.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotfiles/lib-git-prompt.sh b/dotfiles/lib-git-prompt.sh index 8ce1592..a2e79c7 100644 --- a/dotfiles/lib-git-prompt.sh +++ b/dotfiles/lib-git-prompt.sh @@ -124,7 +124,7 @@ __git_ps1_show_upstream() { fi ;; svn-remote.*.url) - svn_remote[${#svn_remote[@]} + 1]="$value" + svn_remote[${#svn_remote[@]}+1]="$value" svn_url_pattern="$svn_url_pattern\\|$value" upstream=svn+git # default upstream is SVN if available, else git ;; @@ -151,7 +151,7 @@ __git_ps1_show_upstream() { mapfile -t svn_upstream < <(git log --first-parent -1 \ --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null) if [[ 0 -ne ${#svn_upstream[@]} ]]; then - local svn_upstream_str=${svn_upstream[${#svn_upstream[@]} - 2]} + local svn_upstream_str=${svn_upstream[${#svn_upstream[@]}-2]} svn_upstream_str=${svn_upstream_str%@*} local n_stop="${#svn_remote[@]}" for ((n = 1; n <= n_stop; n++)); do