This commit is contained in:
Ray Elliott 2026-10-27 13:32:57 +00:00
commit 2ac0f22dba
12 changed files with 346 additions and 10 deletions

View file

@ -10,9 +10,10 @@ the Bash login sequence.
- Configure Bash history, prompt rendering, completion and command-not-found
behaviour.
- Initialize Bash integrations such as NVM, Pyenv, z.lua and gitstatus.
- Own interactive aliases, functions and their curated command catalogue.
- Isolate Arch-specific interactive commands from general Bash configuration.
- Display the startup greeting and cached package status.
- Display `TODO.md` and `.autonote.md` after successful directory changes.
- Load common interactive aliases from the `profile` repository.
## Boundaries
@ -20,8 +21,6 @@ This repository does not own:
- Portable login environment variables or shared PATH construction. Those
belong in the `profile` repository.
- Common aliases intended for more than one Bourne-family shell. Those load
through `profile/interactive.sh`; genuinely Bash-specific aliases live here.
- User services, timers or background refresh logic. Those belong in the
`systemd` repository. Bash may consume their cached output or request a
non-blocking service start.
@ -44,7 +43,16 @@ shell is not interactive.
- `bash_profile`: connects Bash login startup to the shared profile and Bash RC.
- `bashrc`: interactive guard and ordered module loader.
- `bashrc.d/`: numbered interactive Bash modules; lower numbers load first.
- `aliases.bash`: Bash-specific and warning-only transition aliases.
- `interactive.sh`: loads interactive aliases, functions and reminders.
- `aliases.sh`: general interactive aliases and functions, written portably.
- `aliases.bash`: aliases requiring Bash-specific naming or behaviour.
- `commands.sh`: command-catalogue display and validation functions.
- `commands.tsv`: curated command descriptions and reminder visibility.
- `commands.README.md`: format and maintenance instructions for the catalogue.
- `hosts.sh`: SSH destination listing function.
- `memory.tsv`: curated daily shell-memory questions and answers.
- `memory.README.md`: memory-bank format and selection behaviour.
- `platform/arch.sh`: Arch-specific applications and system integration.
- `gitstatus-custom.sh`: prompt integration for gitstatus.
- `cheat.bash`: completion for `cheat`.
- `install.sh`: installs the home-directory symlinks and gitstatus dependency.
@ -59,7 +67,7 @@ The loader sources every readable `bashrc.d/*.bash` file in lexical order:
- `00-core.bash`: colors, history, terminal and Readline settings.
- `10-prompt.bash`: prompt, gitstatus and per-command history flushing.
- `20-greeting.bash`: calendar-day greeting and adjective selection.
- `30-integrations.bash`: shared aliases, NVM and z.lua.
- `30-integrations.bash`: interactive commands, NVM and z.lua.
- `40-status.bash`: command reminders, memory and cached package status.
- `50-completions.bash`: command-specific completion setup.
- `60-directory-notes.bash`: `TODO.md` and `.autonote.md` display after `cd`.
@ -78,5 +86,5 @@ local package transaction, Bash requests a non-blocking start of
`package-status-refresh.service`, subject to a five-minute retry backoff.
The refresh service and 30-minute timer are owned and documented by the
`systemd` repository. The Arch `update` wrapper in the `profile` repository
also requests a refresh after a successful upgrade.
`systemd` repository. The Arch `update` wrapper in this repository also
requests a refresh after a successful upgrade.

79
aliases.sh Normal file
View file

@ -0,0 +1,79 @@
#!/bin/sh
# Interactive aliases and helpers written with portable shell syntax.
alias venv='. ./venv/bin/activate'
alias e='nvim'
alias er='nvim README.md'
alias et='nvim TODO.md'
alias en='nvim NOTES.md'
alias ens='nvim NEXT_STEPS.md'
alias ea='nvim .autonote.md'
alias ee='nvim .'
debuglog() {
clear
: > debug.log
tail -f debug.log
}
alias g='git status'
gg() {
_gg_message=${*:-update}
if git add . && git commit -m "$_gg_message" && git push; then
unset _gg_message
return 0
fi
unset _gg_message
return 1
}
wget() {
printf 'wget wrapper: using HSTS file %s\n' "$XDG_DATA_HOME/wget-hsts" >&2
command wget --hsts-file="$XDG_DATA_HOME/wget-hsts" "$@"
}
alias bc='bc -q -l'
alias ytdl='yt-dlp --write-info-json --write-description --no-playlist'
alias ytdlb='yt-dlp --write-info-json --write-description --no-playlist -a'
alias ytdl-mp3='yt-dlp -f bestaudio --extract-audio --audio-format mp3 --embed-thumbnail --add-metadata'
# lf gets confused by TERMINFO when running in tmux.
# https://github.com/gokcehan/lf/issues/138#issuecomment-464477665
if [ -n "${TERMINFO:-}" ]; then
alias lf='TERMINFO= lf -last-dir-path="$HOME/.cache/lf-last-dir"'
else
alias lf='lf -last-dir-path="$HOME/.cache/lf-last-dir"'
fi
cdlf() {
[ -r "$HOME/.cache/lf-last-dir" ] || return 1
IFS= read -r _cdlf_last_dir < "$HOME/.cache/lf-last-dir"
[ -n "$_cdlf_last_dir" ] || return 1
if cd "$_cdlf_last_dir"; then
unset _cdlf_last_dir
return 0
fi
unset _cdlf_last_dir
return 1
}
lfcd() {
lf -last-dir-path="$HOME/.cache/lf-last-dir" && cdlf
}
mkcd() {
if [ "$#" -ne 1 ]; then
printf '%s\n' 'Usage: mkcd <directory>' >&2
return 2
fi
mkdir -p "$1" || return
cd "$1" || return
}

View file

@ -1,7 +1,9 @@
# shellcheck shell=bash
[ -r "$HOME/.config/profile/interactive.sh" ] && . "$HOME/.config/profile/interactive.sh"
[ -r "$HOME/.config/bash/aliases.bash" ] && . "$HOME/.config/bash/aliases.bash"
_bash_config_home=${XDG_CONFIG_HOME:-$HOME/.config}/bash
[ -r "$_bash_config_home/interactive.sh" ] && . "$_bash_config_home/interactive.sh"
[ -r "$_bash_config_home/aliases.bash" ] && . "$_bash_config_home/aliases.bash"
unset _bash_config_home
export NVM_DIR="$HOME/.nvm"
if [[ -s "$NVM_DIR/nvm.sh" ]]; then

View file

@ -1,6 +1,6 @@
# shellcheck shell=bash
_memory_file="${XDG_CONFIG_HOME:-$HOME/.config}/profile/memory.tsv"
_memory_file="${XDG_CONFIG_HOME:-$HOME/.config}/bash/memory.tsv"
_memory_question_color='\033[38;5;11m'
_memory_names=()
_memory_questions=()

23
commands.README.md Normal file
View file

@ -0,0 +1,23 @@
# Command reminders
`commands.tsv` is the manually curated source for Bash's command reminder.
It is not an inventory of every installed executable.
Each non-empty line contains three tab-separated fields:
```text
reminder command description
```
- `reminder` must be `yes` or `no`. `yes` includes the command in the shell
reminder; `no` keeps it available only in the full listing.
- `command` is the name entered at the shell.
- `description` is a brief explanation displayed by `commands`.
The data file deliberately has no header or comments. Blank lines are allowed;
put explanatory material in this README instead. Duplicate commands, unknown
reminder values and malformed rows are reported as errors.
Run `commands` to display every entry. To add or remove something from the
shell reminder, edit only its `reminder` field; do not duplicate the command
list in shell configuration.

68
commands.sh Normal file
View file

@ -0,0 +1,68 @@
#!/bin/sh
# Display and validate the curated command catalogue.
commands() {
awk -F '\t' '
NF == 0 { next }
NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" {
printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr"
invalid = 1
next
}
seen[$2]++ {
printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr"
invalid = 1
next
}
{
count++
reminder[count] = $1
command[count] = $2
description[count] = $3
if (length($2) > command_width) command_width = length($2)
if (length($3) > description_width) description_width = length($3)
}
END {
if (invalid) exit 1
if (length("COMMAND") > command_width) command_width = length("COMMAND")
if (length("DESCRIPTION") > description_width) description_width = length("DESCRIPTION")
printf "%-*s %-*s %s\n", command_width, "COMMAND", description_width, "DESCRIPTION", "REMINDER"
for (row = 1; row <= count; row++) {
printf "%-*s %-*s %s\n", command_width, command[row], description_width, description[row], reminder[row]
}
}
' "${XDG_CONFIG_HOME:-$HOME/.config}/bash/commands.tsv"
}
_command_reminder() {
awk -F '\t' -v width="${COLUMNS:-80}" '
NF == 0 { next }
NF != 3 || ($1 != "yes" && $1 != "no") || $2 == "" || $3 == "" {
printf "Invalid command reminder on line %d.\n", NR > "/dev/stderr"
invalid = 1
next
}
seen[$2]++ {
printf "Duplicate command reminder for %s on line %d.\n", $2, NR > "/dev/stderr"
invalid = 1
next
}
$1 == "yes" { command[++count] = $2 }
END {
if (invalid) exit 1
if (width < 40) width = 80
line = " "
for (row = 1; row <= count; row++) {
separator = (line == " " ? "" : " ")
if (length(line) + length(separator) + length(command[row]) > width) {
print line
line = " " command[row]
} else {
line = line separator command[row]
}
}
if (line != " ") print line
}
' "${XDG_CONFIG_HOME:-$HOME/.config}/bash/commands.tsv"
}

16
commands.tsv Normal file
View file

@ -0,0 +1,16 @@
yes nap snippet manager
yes hyprpicker Wayland colour picker
yes nmcli NetworkManager command-line client
yes lftp file-transfer client
yes lxc-wp create a local WordPress LXC project
yes qv4l2 Video4Linux control panel
no cheat command-line cheatsheets
no vimv batch rename files in Vim
yes rclone sync files with cloud storage
yes zathura document viewer
yes tesseract optical character recognition
yes remmina remote desktop client
yes wl-copy copy to the Wayland clipboard
yes wl-paste paste from the Wayland clipboard
yes hosts list SSH destinations
yes commands list remembered commands and descriptions
1 yes nap snippet manager
2 yes hyprpicker Wayland colour picker
3 yes nmcli NetworkManager command-line client
4 yes lftp file-transfer client
5 yes lxc-wp create a local WordPress LXC project
6 yes qv4l2 Video4Linux control panel
7 no cheat command-line cheatsheets
8 no vimv batch rename files in Vim
9 yes rclone sync files with cloud storage
10 yes zathura document viewer
11 yes tesseract optical character recognition
12 yes remmina remote desktop client
13 yes wl-copy copy to the Wayland clipboard
14 yes wl-paste paste from the Wayland clipboard
15 yes hosts list SSH destinations
16 yes commands list remembered commands and descriptions

18
hosts.sh Normal file
View file

@ -0,0 +1,18 @@
#!/bin/sh
# List the concrete destinations defined by the user's SSH configuration.
hosts() {
if [ ! -r "$HOME/.ssh/config.d/hosts.conf" ]; then
printf '%s\n' 'SSH host configuration is unavailable.' >&2
return 1
fi
awk '
tolower($1) == "host" {
for (field = 2; field <= NF; field++) {
if ($field !~ /[*?!]/) print $field
}
}
' "$HOME/.ssh/config.d/hosts.conf"
}

20
interactive.sh Normal file
View file

@ -0,0 +1,20 @@
#!/bin/sh
# Interactive Bash setup assembled from focused, mostly portable files.
_bash_interactive_dir=${XDG_CONFIG_HOME:-$HOME/.config}/bash
. "$_bash_interactive_dir/aliases.sh"
. "$_bash_interactive_dir/commands.sh"
. "$_bash_interactive_dir/hosts.sh"
if [ -r /etc/arch-release ]; then
. "$_bash_interactive_dir/platform/arch.sh"
fi
printf '%s\n' 'Remember aliases:'
printf '\033[1;37m%s\n' ' et en ens ee'
printf '%s\n' ' dusort debuglog mkcd'
printf '\033[0m\n\n'
unset _bash_interactive_dir

23
memory.README.md Normal file
View file

@ -0,0 +1,23 @@
# Shell memory questions
`memory.tsv` is a selectively curated question-and-answer bank for reinforcing
useful Bash commands. It deliberately excludes internal functions, standard
command overrides and deprecated transition aliases.
Each non-empty line contains three tab-separated fields:
```text
name question answer
```
- `name` is a unique stable identifier, normally the command or alias name.
- `question` is displayed on shell startup.
- `answer` is displayed by running `answerpls` in that terminal.
The file has no header or comments. Empty fields, duplicate names and malformed
rows are errors.
Bash selects one entry deterministically from the local calendar date. Every
terminal opened on the same day therefore shows the same question without
requiring a state file. Editing the bank may change which question maps to a
given date.

23
memory.tsv Normal file
View file

@ -0,0 +1,23 @@
nap What do you use as a snippet manager? nap
hyprpicker What do you use as a colour picker? hyprpicker
nmcli What manages NetworkManager from the terminal? nmcli
lftp What do you use for interactive file transfers? lftp
lxc-wp What creates a local WordPress LXC project? lxc-wp
qv4l2 What opens the Video4Linux control panel? qv4l2
cheat What displays command-line cheatsheets? cheat
vimv What lets you batch rename files in Vim? vimv
rclone What synchronizes files with cloud storage? rclone
zathura What lightweight application opens documents? zathura
tesseract What performs optical character recognition? tesseract
remmina What opens remote desktop connections? remmina
wl-copy What copies text to the Wayland clipboard? wl-copy
wl-paste What pastes text from the Wayland clipboard? wl-paste
hosts What lists your configured SSH destinations? hosts
commands What lists remembered commands and their descriptions? commands
et What opens TODO.md for editing? et
en What opens NOTES.md for editing? en
ens What opens NEXT_STEPS.md for editing? ens
ee What opens the current directory in Neovim? ee
dusort What displays disk usage sorted by size? dusort [path]
debuglog What clears and follows debug.log? debuglog
mkcd What creates a directory and enters it? mkcd <directory>
1 nap What do you use as a snippet manager? nap
2 hyprpicker What do you use as a colour picker? hyprpicker
3 nmcli What manages NetworkManager from the terminal? nmcli
4 lftp What do you use for interactive file transfers? lftp
5 lxc-wp What creates a local WordPress LXC project? lxc-wp
6 qv4l2 What opens the Video4Linux control panel? qv4l2
7 cheat What displays command-line cheatsheets? cheat
8 vimv What lets you batch rename files in Vim? vimv
9 rclone What synchronizes files with cloud storage? rclone
10 zathura What lightweight application opens documents? zathura
11 tesseract What performs optical character recognition? tesseract
12 remmina What opens remote desktop connections? remmina
13 wl-copy What copies text to the Wayland clipboard? wl-copy
14 wl-paste What pastes text from the Wayland clipboard? wl-paste
15 hosts What lists your configured SSH destinations? hosts
16 commands What lists remembered commands and their descriptions? commands
17 et What opens TODO.md for editing? et
18 en What opens NOTES.md for editing? en
19 ens What opens NEXT_STEPS.md for editing? ens
20 ee What opens the current directory in Neovim? ee
21 dusort What displays disk usage sorted by size? dusort [path]
22 debuglog What clears and follows debug.log? debuglog
23 mkcd What creates a directory and enters it? mkcd <directory>

56
platform/arch.sh Normal file
View file

@ -0,0 +1,56 @@
#!/bin/sh
# Interactive Bash commands specific to this Arch Linux workstation.
_ARCH_GREEN='\033[0;32m'
_ARCH_BLUE='\033[0;34m'
_ARCH_NC='\033[0m'
alias backup='sudo borgmatic create --verbosity 1 --files'
alias cs='lxc list -c ns4b,user.comment:comment'
pc() {
nohup pcmanfm . >/dev/null 2>&1 &
}
# Expand aliases for commands following sudo.
alias sudo='sudo '
alias speak='/usr/local/src/kokora/speak'
alias ls='LC_COLLATE=C ls --color=auto -h --group-directories-first --time-style=+"%d.%m.%Y %H:%M" -F'
alias cp='cp -irv'
alias mkdir='mkdir -vp'
alias mv='mv -iv'
alias rm='rm -dv'
alias grep='grep --colour=auto'
dusort() {
if [ "$#" -gt 1 ]; then
printf '%s\n' 'Usage: dusort [path]' >&2
return 2
fi
_dusort_path=${1:-.}
if [ ! -e "$_dusort_path" ]; then
printf 'dusort: path does not exist: %s\n' "$_dusort_path" >&2
unset _dusort_path
return 1
fi
if sudo du -h -d 2 -- "$_dusort_path" | sort -h; then
unset _dusort_path
return 0
fi
unset _dusort_path
return 1
}
update() {
printf 'Executing "%bpacman -Syu%b" via "%barchnews_wrap%b" wrapper\n' \
"$_ARCH_GREEN" "$_ARCH_NC" "$_ARCH_BLUE" "$_ARCH_NC"
sudo "$HOME/.src/archnews/archnews_wrap" -Syu || return
systemctl --user start --no-block package-status-refresh.service >/dev/null 2>&1 || true
}