feat: implement __rand_string generator and update jobrunner backend #98

Merged
rootiest merged 7 commits from feature/rand-string-generator into main 2026-08-06 19:44:27 +00:00
17 changed files with 3749 additions and 6 deletions
+1
View File
@@ -30,6 +30,7 @@ This config layers on top of the CachyOS base Fish configuration and adds:
- **Smart CLI wrappers** that prefer modern tools (`eza`, `bat`, `btop`, `dust`, `prettyping`) with graceful fallbacks
- **Auto Python venv** activation on directory change (direnv-aware)
- **Kitty terminal** deep integration for splits, tabs, and SSH
- **Named background jobs** — `jobrunner` (short: `jr`) starts, lists, inspects, re-attaches to, and kills detached tasks via `tmux` or GNU `screen`, so long-running work survives closing the shell
- **Optional session logging** — terminal scrollback, multiplexer panes (tmux/zellij), and AUR-helper output can be captured to `~/.terminal_history`; **off by default**, opt in when you want it (see the caution below and [Session Logging](#session-logging))
- **AI workflow** helpers for Claude and Antigravity session management
- **WakaTime** shell activity tracking
+79
View File
@@ -0,0 +1,79 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# Completions for the `jobrunner` background job manager.
# `jr` inherits these via `function jr --wraps jobrunner`.
# Offer running jobs as name<TAB>description pairs.
function __jobrunner_complete_jobs
set -l tool ""
set -l tokens (commandline -opc)
set -l idx 1
while test $idx -le (count $tokens)
switch $tokens[$idx]
case -t --tool
set idx (math $idx + 1)
if test $idx -le (count $tokens)
set tool $tokens[$idx]
end
case '--tool=*'
set tool (string replace -- "--tool=" "" $tokens[$idx])
case '-t*'
set tool (string replace -r "^-t" "" $tokens[$idx])
end
set idx (math $idx + 1)
end
for row in (__jobrunner_sessions $tool)
set -l f (string split \t -- $row)
printf '%s\t%s job (PID %s)\n' $f[1] $f[3] $f[2]
end
end
set -l subcmds run list attach kill logs help
set -l needs_job "__fish_seen_subcommand_from attach kill logs -a --attach -k --kill -o --output"
# No file completions; jobs are named, not paths.
complete -c jobrunner -f
# Backend tool selection flag.
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s t -l tool -x -a "tmux screen" -d 'Force specific backend'
# Subcommands (only as the first argument).
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a run -d 'Start a named job in the background'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a list -d 'List all managed background jobs'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a attach -d 'Re-attach interactively to a job'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a kill -d 'Terminate a running background job'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a logs -d "Print a job's output without attaching"
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a help -d 'Show usage help'
# Flag forms.
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s r -l run -d 'Start a named job in the background'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s l -l list -d 'List all managed background jobs'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s a -l attach -d 'Re-attach interactively to a job'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s k -l kill -d 'Terminate a running background job'
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-s o -l output -d "Print a job's output without attaching"
complete -c jobrunner -s h -l help -d 'Show usage help'
# Running jobs, for the subcommands that take one.
complete -c jobrunner -n "$needs_job" -a '(__jobrunner_complete_jobs)'
# A bare job name attaches to it, so offer jobs in first position too.
complete -c jobrunner -n "not __fish_seen_subcommand_from $subcmds" \
-a '(__jobrunner_complete_jobs)'
# After `run <name>`, complete the command to execute.
complete -c jobrunner -n "__fish_seen_subcommand_from run -r --run; and test (count (commandline -opc)) -ge 3" \
-a '(__fish_complete_subcommand)'
+21
View File
@@ -0,0 +1,21 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
complete -c rand_string -f
complete -c rand_string -s s -l separator -x -a 'dash underscore dot none' -d 'Set separator for following words'
complete -c rand_string -s c -l case -x -a 'lower upper title' -d 'Set casing for following words'
complete -c rand_string -s h -l help -d 'Show usage help'
# List of all available lists in data/words
set -l categories
if set -q __fish_config_dir
set categories (string replace -r '\.txt$' '' (command ls $__fish_config_dir/data/words/*.txt 2>/dev/null | command xargs -n 1 basename 2>/dev/null))
end
for cat in $categories
complete -c rand_string -a "$cat" -d "Pick a random word from the $cat list"
end
complete -c rand_string -a 'digits=' -d 'Generate N random digits (e.g. digits=3)'
complete -c rand_string -a 'literal=' -d 'Insert a literal string exactly as provided'
+108
View File
@@ -0,0 +1,108 @@
admiring
adoring
affectionate
agitated
amazing
angry
awesome
beautiful
blissful
bold
boring
brave
busy
charming
clever
compassionate
competent
condescending
confident
cool
cranky
crazy
dazzling
determined
distracted
dreamy
eager
ecstatic
elastic
elated
elegant
eloquent
epic
exciting
fervent
festive
flamboyant
focused
friendly
frosty
funny
gallant
gifted
goofy
gracious
great
happy
hardcore
heuristic
hopeful
hungry
infallible
inspiring
intelligent
interesting
jolly
jovial
keen
kind
laughing
loving
lucid
magical
modest
musing
mystifying
naughty
nervous
nice
nifty
nostalgic
objective
optimistic
peaceful
pedantic
pensive
practical
priceless
quirky
quizzical
recursing
relaxed
reverent
romantic
sad
serene
sharp
silly
sleepy
stoic
strange
stupefied
suspicious
sweet
tender
thirsty
trusting
unruffled
upbeat
vibrant
vigilant
vigorous
wizardly
wonderful
xenodochial
youthful
zealous
zen
+223
View File
@@ -0,0 +1,223 @@
aardvark
albatross
alligator
alpaca
ant
anteater
antelope
ape
armadillo
baboon
badger
barracuda
bat
bear
beaver
bee
bison
boar
buffalo
butterfly
camel
capybara
caribou
cat
caterpillar
cattle
chamois
cheetah
chicken
chimpanzee
chinchilla
chough
clam
cobra
cockroach
cod
cormorant
coyote
crab
crane
crocodile
crow
curlew
deer
dinosaur
dog
dogfish
dolphin
donkey
dotterel
dove
dragonfly
duck
dugong
dunlin
eagle
echidna
eel
eland
elephant
elk
emu
falcon
ferret
finch
fish
flamingo
fly
fox
frog
gaur
gazelle
gerbil
giraffe
gnat
gnu
goat
goldfinch
goldfish
goose
gorilla
goshawk
grasshopper
grouse
guanaco
gull
hamster
hare
hawk
hedgehog
heron
herring
hippopotamus
hornet
horse
human
hummingbird
hyena
ibex
ibis
jackal
jaguar
jay
jellyfish
kangaroo
kingfisher
koala
kookabura
kouprey
kudu
lapwing
lark
lemur
leopard
lion
llama
lobster
locust
loris
louse
lyrebird
magpie
mallard
manatee
mandrill
mantis
marten
meerkat
mink
mole
mongoose
monkey
moose
mosquito
mouse
mule
narwhal
newt
nightingale
octopus
okapi
opossum
oryx
ostrich
otter
owl
oyster
panther
parrot
partridge
peafowl
pelican
penguin
pheasant
pig
pigeon
pony
porcupine
porpoise
quail
quelea
quetzal
rabbit
raccoon
rail
ram
rat
raven
red-deer
red-panda
reindeer
rhinoceros
rook
salamander
salmon
sand-dollar
sandpiper
sardine
scorpion
seahorse
seal
shark
sheep
shrew
skunk
snail
snake
sparrow
spider
spoonbill
squid
squirrel
starling
stingray
stinkbug
stork
swallow
swan
tapir
tarsier
termite
tiger
toad
trout
turkey
turtle
viper
vulture
wallaby
walrus
wasp
weasel
whale
wildcat
wolf
wolverine
wombat
woodcock
woodpecker
worm
wren
yak
zebra
+81
View File
@@ -0,0 +1,81 @@
amber
amethyst
apricot
aqua
aquamarine
auburn
azure
beige
black
blue
bronze
brown
buff
carmine
celadon
cerise
cerulean
charcoal
chartreuse
chocolate
cinnamon
copper
coral
cream
crimson
cyan
denim
emerald
fuchsia
gold
goldenrod
gray
green
grey
indigo
ivory
jade
khaki
lavender
lemon
lilac
lime
magenta
mahogany
maroon
mauve
mustard
navy
ochre
olive
orange
orchid
peach
pear
periwinkle
pink
plum
puce
purple
quartz
red
rose
ruby
rust
salmon
sapphire
scarlet
silver
slate
tan
taupe
teal
tomato
topaz
turquoise
ultramarine
umber
vanilla
violet
white
yellow
+236
View File
@@ -0,0 +1,236 @@
agnesi
albattani
allen
almeida
antonelli
archimedes
ardinghelli
aryabhata
austin
babbage
banach
banzai
bardeen
bartik
bassi
beaver
bell
benz
bhabha
bhaskara
black
blackburn
blackwell
bohr
booth
borg
bose
bouman
boyd
brahmagupta
brattain
brown
buck
burnell
cannon
carson
cartwright
carver
cerf
chandrasekhar
chaplygin
chatelet
chatterjee
chaum
chebyshev
clarke
cohen
colden
cori
cray
curie
curran
darwin
davinci
dewdney
dhawan
diffie
dijkstra
dirac
driscoll
dubinsky
easley
edison
einstein
elbakyan
elgamal
elion
ellis
engelbart
euclid
euler
faraday
feistel
fermat
fermi
feynman
franklin
gagarin
galileo
galois
ganguly
gates
gauss
germain
goldberg
goldstine
goldwasser
golick
goodall
gould
greider
grothendieck
haibt
hamilton
haslett
hawking
heisenberg
hellman
hermann
herschel
hertz
heyrovsky
hodgkin
hofstadter
hoover
hopper
hugle
hypatia
ishizaka
jackson
jang
jemison
jennings
jepsen
johnson
joliot
jones
kalam
kapitsa
kare
keldysh
keller
kepler
khayyam
khorana
kilby
kirch
knuth
kowalevski
lalande
lamarr
lamport
leakey
leavitt
lederberg
lehmann
lewin
lichterman
liskov
lovelace
lumiere
mahavira
margulis
matsumoto
maxwell
mayer
mccarthy
mcclintock
mclaren
mclean
mcnulty
meitner
mendel
mendeleev
meninsky
merkle
mestorf
mirzakhani
montalcini
moore
morse
moser
murdock
napier
nash
neumann
newton
nightingale
nobel
noether
northcutt
noyce
panini
pare
pascal
pasteur
payne
perlman
pike
poincare
poitras
proskuriakova
ptolemy
raman
ramanujan
rhodes
ride
ritchie
robinson
roentgen
rosalind
rubin
saha
sammet
sanderson
satoshi
shamir
shannon
shaw
shirley
shockley
shtern
sinoussi
snyder
solomon
spence
stonebraker
sutherland
swanson
swartz
swirles
taussig
tesla
tharp
thompson
torvalds
tu
turing
varahamihira
vaughan
villani
visvesvaraya
volhard
wescoff
wilbur
wiles
williams
williamson
wilson
wing
wozniak
wright
wu
yalow
yonath
zhukovsky
+1296
View File
File diff suppressed because it is too large Load Diff
+1000
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -125,6 +125,8 @@ split=### split
spwin=### spwin
detach=### detach
bkg=### bkg
jobrunner=### jobrunner
jr=### jr
ssh=### ssh
clipboard=## 5.9 Clipboard
copy-fn=### y
+65
View File
@@ -1482,6 +1482,71 @@ Add -i (interactive confirmation) to destructive commands:
Example:
# Rendered automatically by fish; not called directly.
### jobrunner
Synopsis: jobrunner [<subcommand>] [<name>] [<command>...]
jr [<subcommand>] [<name>] [<command>...]
Runs, lists, inspects, re-attaches to, and terminates named background
jobs using GNU screen as the process engine. Unlike bkg and detach,
which discard output, a jobrunner job keeps a live terminal you can
return to later — it survives closing the shell, and `attach` restores
it in any subsequent session.
Every subcommand has a matching flag form, and the common cases are
inferred: no arguments lists jobs, a lone name attaches to it, and a
name followed by a command runs it.
Arguments:
run, -r, --run <name> <cmd>... Start a named job in the background
list, -l, --list List all managed background jobs
attach, -a, --attach <name> Re-attach interactively to a job
kill, -k, --kill <name> Terminate a running background job
logs, -o, --output <name> Print a job's current output, no attach
help, -h, --help Show usage help
Exit Status:
0 Command succeeded, or no jobs are running
1 Invalid arguments, or the named job does not exist
127 screen is not installed
Notes:
Detach from an attached job with Ctrl-A then D; the job keeps running.
Commands are executed directly rather than through a shell, so pipes and
redirections must be wrapped explicitly, e.g.
`jobrunner run sync fish -c 'a | b'`.
Example:
jobrunner run build make -j8
jobrunner backup rsync -a ./data remote:/backup/
jobrunner list
jobrunner logs build
jobrunner build
jobrunner kill build
**Dependencies:** `screen`, `__jobrunner_sessions`
**Used by:** `jr`
### jr
Synopsis: jr [<subcommand>] [<name>] [<command>...]
Shorthand for jobrunner. Accepts the same subcommands, flags, and
shorthands, and inherits its completions.
Arguments:
See jobrunner --help for the full argument reference.
Exit Status:
Same as jobrunner.
Example:
jr run build make -j8
jr list
**Dependencies:** `jobrunner`
### split
Synopsis: split [-h | -v] [command...]
+64
View File
@@ -0,0 +1,64 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# __jobrunner_sessions [<tool>]
#
# DESCRIPTION
# Parses `tmux list-sessions` or `screen -ls` into machine-readable rows,
# one per active session: name, PID, state, and start time separated by tabs.
# Shared by jobrunner and its completions so both agree on what a session is
# named. Prints nothing when no sessions exist.
#
# EXIT STATUS
# 0 Parsed successfully (including the zero-session case)
# 1 tool (tmux or screen) is not installed
#
# RETURNS
# One line per session: <name>\t<pid>\t<state>\t<started>
#
# EXAMPLE
# __jobrunner_sessions
# __jobrunner_sessions tmux | string replace -r '\t.*$' ''
function __jobrunner_sessions --description 'List active jobrunner sessions as name/pid/state/started rows'
set -l tool $argv[1]
if test -z "$tool"
if command -q tmux
set tool tmux
else if command -q screen
set tool screen
else
return 1
end
end
if test "$tool" = tmux
command -q tmux; or return 1
set -l tab (printf '\t')
for line in (command tmux list-sessions -F "#{session_name}$tab#{pid}$tab#{?session_attached,Attached,Detached}$tab#{t:session_created}" 2>/dev/null)
# tmux output is natively tab-separated.
printf '%s\n' $line
end
else if test "$tool" = screen
command -q screen; or return 1
# screen -ls exits 1 when nothing is running; the parse handles that.
for line in (command screen -ls 2>/dev/null)
# Session rows are tab-indented and start with "<pid>.<name>".
set -l m (string match -r '^\s+(\d+)\.([^\t]+)\t(.*)$' -- $line)
or continue
# Trailing fields are parenthesized: "(<date>)\t(<state>)", and some
# screen builds omit the date, so read the state off the end.
set -l fields (string split \t -- $m[4])
set -l state (string trim -c '()' -- $fields[-1])
set -l started ""
if test (count $fields) -gt 1
set started (string trim -c '()' -- $fields[1])
end
printf '%s\t%s\t%s\t%s\n' $m[3] $m[2] $state $started
end
else
return 1
end
end
+5 -5
View File
@@ -16,27 +16,27 @@ function _fish_deps_catalog
set -g _fdc_bins \
uv cargo fish starship fzf zoxide direnv paru yay \
wakatime tailscale \
eza lsd bat btop dust duf prettyping ov rg lazygit lazydocker trash kitty wezterm python3 yt-dlp
eza lsd bat btop dust duf prettyping ov rg lazygit lazydocker trash kitty wezterm python3 yt-dlp screen
set -g _fdc_tiers \
rec rec req rec req rec rec rec rec \
int int \
rec rec rec rec rec rec rec rec rec rec rec rec rec rec rec rec
rec rec rec rec rec rec rec rec rec rec rec rec rec rec rec rec rec
set -g _fdc_cargo \
"" "" "" starship "" zoxide "" "" "" \
"" "" \
eza lsd bat "" du-dust "" "" ov ripgrep "" "" trashy "" "" "" ""
eza lsd bat "" du-dust "" "" ov ripgrep "" "" trashy "" "" "" "" ""
set -g _fdc_pm \
uv cargo fish starship fzf zoxide direnv "" yay \
wakatime tailscale \
eza lsd bat btop dust duf prettyping ov ripgrep lazygit lazydocker trash kitty wezterm python yt-dlp
eza lsd bat btop dust duf prettyping ov ripgrep lazygit lazydocker trash kitty wezterm python yt-dlp screen
set -g _fdc_special \
curl-uv rustup-installer git-cargo-fish curl-installer fzf-update "" "" paru-build yay-build \
wakatime-binary "" \
"" "" "" "" "" "" "" "" "" "" curl-lazydocker "" "" "" "" ""
"" "" "" "" "" "" "" "" "" "" curl-lazydocker "" "" "" "" "" ""
end
# SYNOPSIS
+354
View File
@@ -0,0 +1,354 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# CATEGORY
# 08-terminal-management
#
# DEPENDENCIES
# tmux, screen, __jobrunner_sessions
#
# SYNOPSIS
# jobrunner [-t <tool>] [<subcommand>] [<name>] [<command>...]
# jr [-t <tool>] [<subcommand>] [<name>] [<command>...]
#
# DESCRIPTION
# Runs, lists, inspects, re-attaches to, and terminates named background
# jobs using tmux or GNU screen as the process engine. Unlike bkg and
# detach, which discard output, a jobrunner job keeps a live terminal you
# can return to later — it survives closing the shell, and `attach`
# restores it in any subsequent session.
# Run and manage named background jobs. Jobs are detached from the shell
# and backed by tmux (preferred) or GNU screen.
#
# If the job name is omitted when starting a new job (e.g. `jobrunner sleep 1`),
# a memorable, random name (like `sleepy-badger`) will be generated.
#
# SUBCOMMANDS
# run, -r, --run [-n <name>] <cmd> Start a new background job
# list, -l, --list List all running jobs (default if no args)
# attach, -a, --attach <name> Attach to a running job's terminal
# kill, -k, --kill <name> Terminate a running background job
# logs, -o, --output <name> Print a job's current output, no attach
# help, -h, --help Show usage help
#
# EXIT STATUS
# 0 Command succeeded, or no jobs are running
# 1 Invalid arguments, or the named job does not exist
# 127 neither tmux nor screen is installed
#
# EXAMPLE
# jobrunner run -n build make -j8
# jobrunner sleep 1000
# jobrunner -t screen run -n backup rsync -a ./data remote:/backup/
# jobrunner list
# jobrunner logs build
# jobrunner build
# jobrunner kill build
#
# NOTES
# Detach from an attached job with Ctrl-A then D; the job keeps running.
# Commands are executed directly rather than through a shell, so pipes and
# redirections must be wrapped explicitly, e.g.
# `jobrunner run sync fish -c 'a | b'`.
function jobrunner --description 'Manage detached background jobs with tmux or GNU screen'
set -l c_head (set_color --bold cyan)
set -l c_cmd (set_color --bold white)
set -l c_arg (set_color cyan)
set -l c_flag (set_color yellow)
set -l c_ok (set_color green)
set -l c_err (set_color red)
set -l c_dim (set_color brblack)
set -l c_rst (set_color normal)
set -l subcmds run list attach kill logs help \
-r --run -l --list -a --attach -k --kill -o --output -h --help
# ╭──────────────────────────────────────────────────────────╮
# │ Tool Extraction │
# ╰──────────────────────────────────────────────────────────╯
set -l tool ""
set -l global_name ""
while test (count $argv) -gt 0
switch $argv[1]
case -t
set tool $argv[2]
set -e argv[1..2]
case --tool
set tool $argv[2]
set -e argv[1..2]
case '--tool=*'
set tool (string replace -- "--tool=" "" $argv[1])
set -e argv[1]
case '-t*'
set tool (string replace -r "^-t" "" $argv[1])
set -e argv[1]
case -n
set global_name $argv[2]
set -e argv[1..2]
case --name
set global_name $argv[2]
set -e argv[1..2]
case '--name=*'
set global_name (string replace -- "--name=" "" $argv[1])
set -e argv[1]
case '-n*'
set global_name (string replace -r "^-n" "" $argv[1])
set -e argv[1]
case '*'
break
end
end
# ╭──────────────────────────────────────────────────────────╮
# │ Help │
# ╰──────────────────────────────────────────────────────────╯
# Answered before the dependency check so usage is readable anywhere.
if set -q argv[1]; and contains -- $argv[1] help -h --help
echo "$c_head""Usage:$c_rst $c_cmd""jobrunner$c_rst $c_arg""[<subcommand>] [<name>] [<command>...]$c_rst"
echo
echo " Run and manage named background jobs backed by tmux or GNU screen."
echo " If $c_arg<name>$c_rst is omitted, a random memorable name is generated."
echo
echo "$c_head""Subcommands:$c_rst"
echo " $c_cmd""run$c_rst, $c_flag-r$c_rst, $c_flag--run$c_rst $c_arg""[-n <name>] <cmd>$c_rst Start a new background job"
echo " $c_cmd""list$c_rst, $c_flag-l$c_rst, $c_flag--list$c_rst List all running jobs (default)"
echo " $c_cmd""attach$c_rst, $c_flag-a$c_rst, $c_flag--attach$c_rst $c_arg""<name>$c_rst Attach to a running job's terminal"
echo " $c_cmd""kill$c_rst, $c_flag-k$c_rst, $c_flag--kill$c_rst $c_arg""<name>$c_rst Terminate a running background job"
echo " $c_cmd""logs$c_rst, $c_flag-o$c_rst, $c_flag--output$c_rst $c_arg""<name>$c_rst Print a job's current output, no attach"
echo " $c_cmd""help$c_rst, $c_flag-h$c_rst, $c_flag--help$c_rst Show usage help"
echo
echo "$c_head""Options:$c_rst"
echo " $c_flag-t$c_rst, $c_flag--tool$c_rst $c_arg""<tool>$c_rst Force backend tool ('tmux' or 'screen')"
echo " $c_flag-n$c_rst, $c_flag--name$c_rst $c_arg""<name>$c_rst Set explicit name for a new job"
echo
echo "$c_head""Shortcuts:$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""<name>$c_rst $c_dim""attach <name>$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""[-n <name>] <cmd>...$c_rst$c_dim""run [-n <name>] <cmd>...$c_rst"
echo
echo "$c_head""Examples:$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""run -n build$c_rst""$c_dim"" make -j8$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""sleep 1000$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""logs build$c_rst"
echo " $c_cmd""jobrunner$c_rst $c_arg""kill build$c_rst"
echo
echo "$c_dim""Detach from an attached job with Ctrl-A then D.$c_rst"
return 0
end
# ╭──────────────────────────────────────────────────────────╮
# │ Dependency check │
# ╰──────────────────────────────────────────────────────────╯
if test -n "$tool"
if not contains -- $tool tmux screen
echo "$c_err""jobrunner:$c_rst invalid tool '$c_arg$tool$c_rst', must be 'tmux' or 'screen'." >&2
return 1
end
if not command -q $tool
echo "$c_err""jobrunner:$c_rst '$tool' is required but was not found in PATH." >&2
return 127
end
else
if command -q tmux
set tool tmux
else if command -q screen
set tool screen
else
echo "$c_err""jobrunner:$c_rst neither 'tmux' nor 'screen' was found in PATH." >&2
return 127
end
end
# ╭──────────────────────────────────────────────────────────╮
# │ Argument pre-processing │
# ╰──────────────────────────────────────────────────────────╯
set -q argv[1]; or set argv list
set -l cmd $argv[1]
if not contains -- $cmd $subcmds
if test (count $argv) -eq 1
# A lone name attaches, but only if that job actually exists.
if contains -- $cmd (__jobrunner_sessions $tool | string replace -r '\t.*$' '')
set argv attach $argv
set cmd attach
else
echo "$c_err""jobrunner:$c_rst unknown subcommand or job '$c_arg$cmd$c_rst'." >&2
echo "Run $c_cmd""jobrunner --help$c_rst for usage." >&2
return 1
end
else
# A name plus a command line is an implicit run.
set argv run $argv
set cmd run
end
end
# ╭──────────────────────────────────────────────────────────╮
# │ Dispatch │
# ╰──────────────────────────────────────────────────────────╯
switch $cmd
case run -r --run
set -l name "$global_name"
set -l task
while test (count $argv) -gt 1
switch $argv[2]
case -n --name
set name $argv[3]
set -e argv[2..3]
case '--name=*'
set name (string replace -- "--name=" "" $argv[2])
set -e argv[2]
case '-n*'
set name (string replace -r "^-n" "" $argv[2])
set -e argv[2]
case '*'
break
end
end
if test (count $argv) -lt 2
echo "$c_head""Usage:$c_rst $c_cmd""jobrunner run$c_rst $c_arg""[-n <name>] <command>...$c_rst" >&2
return 1
end
if test -z "$name"
set name (rand_string adjective animal)
end
set task $argv[2..-1]
# screen stores each session as a socket file named after it.
if string match -q '*/*' -- $name
echo "$c_err""jobrunner:$c_rst job name may not contain '/'." >&2
return 1
end
if contains -- $name (__jobrunner_sessions $tool | string replace -r '\t.*$' '')
echo "$c_err""jobrunner:$c_rst job '$c_arg$name$c_rst' is already running." >&2
return 1
end
if test "$tool" = tmux
command tmux new-session -d -s $name $task
else
command screen -d -m -S $name $task
end
or begin
echo "$c_err""jobrunner:$c_rst failed to start job '$c_arg$name$c_rst'." >&2
return 1
end
set -l pid
for row in (__jobrunner_sessions $tool)
set -l f (string split \t -- $row)
test "$f[1]" = "$name"; and set pid $f[2]; and break
end
if set -q pid[1]
echo "$c_ok""$c_rst Started job $c_arg$name$c_rst $c_dim(PID $pid)$c_rst"
else
# The job may have finished (or failed) before we looked.
echo "$c_ok""$c_rst Started job $c_arg$name$c_rst $c_dim(already exited)$c_rst"
end
case list -l --list
set -l rows (__jobrunner_sessions $tool)
if test (count $rows) -eq 0
echo "No background jobs running."
return 0
end
printf '%s%-20s %-8s %-10s %s%s\n' "$c_head" JOB PID STATE STARTED "$c_rst"
for row in $rows
set -l f (string split \t -- $row)
printf '%s%-20s%s %-8s %-10s %s%s%s\n' \
"$c_arg" $f[1] "$c_rst" $f[2] $f[3] "$c_dim" $f[4] "$c_rst"
end
case attach -a --attach
if test (count $argv) -ne 2
echo "$c_head""Usage:$c_rst $c_cmd""jobrunner attach$c_rst $c_arg""<name>$c_rst" >&2
return 1
end
set -l name $argv[2]
if not contains -- $name (__jobrunner_sessions $tool | string replace -r '\t.*$' '')
echo "$c_err""jobrunner:$c_rst no such job '$c_arg$name$c_rst'." >&2
return 1
end
if test "$tool" = tmux
command tmux attach-session -t $name
else
# -x attaches to an already-attached session instead of failing.
command screen -x $name
end
case kill -k --kill
if test (count $argv) -ne 2
echo "$c_head""Usage:$c_rst $c_cmd""jobrunner kill$c_rst $c_arg""<name>$c_rst" >&2
return 1
end
set -l name $argv[2]
if not contains -- $name (__jobrunner_sessions $tool | string replace -r '\t.*$' '')
echo "$c_err""jobrunner:$c_rst no such job '$c_arg$name$c_rst'." >&2
return 1
end
if test "$tool" = tmux
command tmux kill-session -t $name
else
command screen -X -S $name quit
end
or begin
echo "$c_err""jobrunner:$c_rst failed to terminate job '$c_arg$name$c_rst'." >&2
return 1
end
echo "$c_ok""$c_rst Terminated job $c_arg$name$c_rst"
case logs -o --output
if test (count $argv) -ne 2
echo "$c_head""Usage:$c_rst $c_cmd""jobrunner logs$c_rst $c_arg""<name>$c_rst" >&2
return 1
end
set -l name $argv[2]
if not contains -- $name (__jobrunner_sessions $tool | string replace -r '\t.*$' '')
echo "$c_err""jobrunner:$c_rst no such job '$c_arg$name$c_rst'." >&2
return 1
end
set -l out
if test "$tool" = tmux
# capture-pane -S - -p outputs the scrollback and current pane directly to stdout.
set out (command tmux capture-pane -t $name -S - -p)
or begin
echo "$c_err""jobrunner:$c_rst could not read output of '$c_arg$name$c_rst'." >&2
return 1
end
else
# hardcopy -h dumps scrollback plus the visible screen to a file.
# ponytail: snapshot only; add `screen -L` logging if full
# since-start history is ever needed.
set -l dump (command mktemp)
command screen -X -S $name hardcopy -h $dump
or begin
command rm -f $dump
echo "$c_err""jobrunner:$c_rst could not read output of '$c_arg$name$c_rst'." >&2
return 1
end
set out (command cat $dump)
command rm -f $dump
end
# hardcopy (and sometimes tmux) pad the dump with empty lines.
while set -q out[1]; and test -z "$out[-1]"
set -e out[-1]
end
if set -q out[1]
printf '%s\n' $out
else
echo "$c_dim(no output yet)$c_rst"
end
case '*'
echo "$c_err""jobrunner:$c_rst invalid subcommand '$c_arg$cmd$c_rst'." >&2
echo "Run $c_cmd""jobrunner --help$c_rst for usage." >&2
return 1
end
end
+28
View File
@@ -0,0 +1,28 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# CATEGORY
# 08-terminal-management
#
# DEPENDENCIES
# jobrunner
#
# SYNOPSIS
# jr [<subcommand>] [<name>] [<command>...]
#
# DESCRIPTION
# Shorthand for jobrunner. Accepts the same subcommands, flags, and
# shorthands, and inherits its completions.
#
# ARGUMENTS
# See jobrunner --help for the full argument reference.
#
# EXIT STATUS
# Same as jobrunner.
#
# EXAMPLE
# jr run build make -j8
# jr list
function jr --wraps jobrunner --description 'Shorthand for jobrunner'
jobrunner $argv
end
+1 -1
View File
@@ -18,7 +18,7 @@
# lstree ~/projects/myapp
function lstree --description 'Full recursive tree listing'
if which eza >/dev/null 2>&1
eza --tree --icons --color=auto --hyperlink $argv
eza --tree --icons --color=auto --hyperlink=auto $argv
else if which lsd >/dev/null 2>&1
lsd --tree --hyperlink=auto $argv
else
+185
View File
@@ -0,0 +1,185 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# CATEGORY
# 14-miscellaneous
#
# SYNOPSIS
# rand_string [COMPONENTS/MODIFIERS]...
#
# DESCRIPTION
# Generates a random, memorable string using a sequence of specified word
# categories and formatting modifiers. Words are pulled from curated
# plain-text databases bundled in `data/words/`.
#
# Modifiers like `--separator` and `--case` are evaluated sequentially and
# apply only to the components that follow them.
#
# Supported Components:
# <category> A bundled word list (e.g. adjective, animal, color, name, noun, verb)
# digits=<N> N random digits (e.g. digits=3 -> 842)
# literal=<text> A static string component (e.g. literal=TEST)
#
# ARGUMENTS
# -s, --separator=<sep> Delimiter for subsequent words (dash, underscore, dot, none, or literal chars)
# -c, --case=<casing> Casing for subsequent words (lower, upper, title)
# -h, --help Show usage help
#
# EXIT STATUS
# 0 String generated successfully
# 1 Unknown category or missing word list file
#
# EXAMPLE
# rand_string adjective animal
# rand_string --case=title color animal --separator=dot digits=4
# rand_string literal=TEST --separator=underscore verb noun
#
# NOTES
# Falls back to `random choice` if GNU `shuf` is missing, but `shuf` is
# much faster for files with >1000 lines.
function rand_string --description 'Generate random, memorable strings from curated word databases'
set -l c_head (set_color --bold cyan)
set -l c_cmd (set_color --bold white)
set -l c_arg (set_color cyan)
set -l c_flag (set_color yellow)
set -l c_rst (set_color normal)
if set -q argv[1]; and contains -- $argv[1] -h --help
echo "$c_head""Usage:$c_rst $c_cmd""rand_string$c_rst $c_arg""[COMPONENTS/MODIFIERS]...$c_rst"
echo
echo " Generate random, memorable strings from curated word databases."
echo
echo "$c_head""Components:$c_rst"
echo " $c_arg<category>$c_rst A bundled word list (e.g. adjective, animal, color, name, noun, verb)"
echo " $c_arg""digits=<N>$c_rst N random digits (e.g. digits=3 -> 842)"
echo " $c_arg""literal=<text>$c_rst A static string component for prefixes/suffixes (e.g. literal=TEST)"
echo
echo "$c_head""Modifiers:$c_rst"
echo " $c_flag-s$c_rst, $c_flag--separator=<sep>$c_rst Delimiter for subsequent words (dash, underscore, dot, none)"
echo " $c_flag-c$c_rst, $c_flag--case=<casing>$c_rst Casing for subsequent words (lower, upper, title)"
echo " $c_flag-h$c_rst, $c_flag--help$c_rst Show usage help"
echo
echo "$c_head""Examples:$c_rst"
echo " $c_cmd""rand_string$c_rst adjective animal"
echo " $c_cmd""rand_string$c_rst --case=title color animal --separator=dot digits=4"
echo " $c_cmd""rand_string$c_rst literal=TEST --separator=underscore verb noun"
return 0
end
set -l sep "dash"
set -l casing "lower"
# Locate the words directory relative to this function
set -l words_dir ""
if set -q __fish_config_dir
set words_dir "$__fish_config_dir/data/words"
else
# Fallback if __fish_config_dir isn't available
set words_dir "$HOME/.config/fish/data/words"
end
set -l result ""
set -l i 1
while test $i -le (count $argv)
set -l arg $argv[$i]
set -l handled 0
# 1. Parse inline modifiers
switch $arg
case -s --separator
set i (math $i + 1)
set sep $argv[$i]
set handled 1
case '--separator=*'
set sep (string replace -- "--separator=" "" $arg)
set handled 1
case '-s*'
set sep (string replace -r "^-s" "" $arg)
set handled 1
case -c --case
set i (math $i + 1)
set casing $argv[$i]
set handled 1
case '--case=*'
set casing (string replace -- "--case=" "" $arg)
set handled 1
case '-c*'
set casing (string replace -r "^-c" "" $arg)
set handled 1
end
if test $handled -eq 1
set i (math $i + 1)
continue
end
# 2. Resolve separator alias
set -l actual_sep $sep
switch $sep
case dash
set actual_sep "-"
case underscore
set actual_sep "_"
case dot
set actual_sep "."
case none empty
set actual_sep ""
end
# 3. Generate the component part
set -l part ""
switch $arg
case 'digits=*'
set -l n (string replace -- "digits=" "" $arg)
if not string match -qr '^\d+$' -- $n
set n 1
end
for idx in (seq $n)
set part "$part"(random 0 9)
end
case 'literal=*'
set part (string replace -- "literal=" "" $arg)
case 'string=*'
set part (string replace -- "string=" "" $arg)
case '*'
set -l db "$words_dir/$arg.txt"
if not test -f "$db"
echo "rand_string: unknown category or file missing for '$arg'" >&2
return 1
end
# Fetch random line (shuf is fastest, random choice is portable fallback)
if command -q shuf
set part (command shuf -n 1 "$db")
else
set part (random choice (command cat "$db"))
end
end
# 4. Apply casing (skip for literal text)
if not string match -q 'literal=*' -- $arg; and not string match -q 'string=*' -- $arg
switch $casing
case upper
set part (string upper $part)
case lower
set part (string lower $part)
case title
set part (string replace -r '^(.)' '\U$1' (string lower $part))
end
end
# 5. Append to result
if test -n "$result"
set result "$result$actual_sep$part"
else
set result "$part"
end
set i (math $i + 1)
end
if test -n "$result"
echo $result
end
end