feat: implement __rand_string generator and update jobrunner backend #98
@@ -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
|
||||
|
||||
@@ -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)'
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1000
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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...]
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user