From ffff95f3b9cf5c8ef1f9f44d07f6f94ce3c791f6 Mon Sep 17 00:00:00 2001 From: rootiest Date: Sun, 2 Aug 2026 05:21:21 -0400 Subject: [PATCH] feat: implement __rand_string generator and update jobrunner to use tmux --- README.md | 2 +- completions/__rand_string.fish | 20 + completions/jobrunner.fish | 24 +- data/words/adjective.txt | 108 +++ data/words/animal.txt | 223 +++++ data/words/color.txt | 81 ++ data/words/name.txt | 236 +++++ data/words/noun.txt | 1296 +++++++++++++++++++++++++++ functions/__jobrunner_sessions.fish | 69 +- functions/__rand_string.fish | 150 ++++ functions/jobrunner.fish | 135 ++- functions/lstree.fish | 2 +- 12 files changed, 2283 insertions(+), 63 deletions(-) create mode 100644 completions/__rand_string.fish create mode 100644 data/words/adjective.txt create mode 100644 data/words/animal.txt create mode 100644 data/words/color.txt create mode 100644 data/words/name.txt create mode 100644 data/words/noun.txt create mode 100644 functions/__rand_string.fish diff --git a/README.md b/README.md index 4df41f5..fadda05 100644 --- a/README.md +++ b/README.md @@ -30,7 +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 GNU `screen`, so long-running work survives closing the shell +- **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 diff --git a/completions/__rand_string.fish b/completions/__rand_string.fish new file mode 100644 index 0000000..c193d1f --- /dev/null +++ b/completions/__rand_string.fish @@ -0,0 +1,20 @@ +# 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' + +# 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' diff --git a/completions/jobrunner.fish b/completions/jobrunner.fish index 52a1acd..c77b8fa 100644 --- a/completions/jobrunner.fish +++ b/completions/jobrunner.fish @@ -6,7 +6,25 @@ # Offer running jobs as namedescription pairs. function __jobrunner_complete_jobs - for row in (__jobrunner_sessions) + 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 @@ -18,6 +36,10 @@ set -l needs_job "__fish_seen_subcommand_from attach kill logs -a --attach -k -- # 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' diff --git a/data/words/adjective.txt b/data/words/adjective.txt new file mode 100644 index 0000000..c4466f4 --- /dev/null +++ b/data/words/adjective.txt @@ -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 diff --git a/data/words/animal.txt b/data/words/animal.txt new file mode 100644 index 0000000..2484c56 --- /dev/null +++ b/data/words/animal.txt @@ -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 diff --git a/data/words/color.txt b/data/words/color.txt new file mode 100644 index 0000000..64fead9 --- /dev/null +++ b/data/words/color.txt @@ -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 diff --git a/data/words/name.txt b/data/words/name.txt new file mode 100644 index 0000000..f2c9896 --- /dev/null +++ b/data/words/name.txt @@ -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 diff --git a/data/words/noun.txt b/data/words/noun.txt new file mode 100644 index 0000000..4c8baa4 --- /dev/null +++ b/data/words/noun.txt @@ -0,0 +1,1296 @@ +acid +acorn +acre +acts +afar +affix +aged +agent +agile +aging +agony +ahead +aide +aids +aim +ajar +alarm +alias +alibi +alien +alike +alive +aloe +aloft +aloha +alone +amend +amino +ample +amuse +angel +anger +angle +ankle +apple +april +apron +aqua +area +arena +argue +arise +armed +armor +army +aroma +array +arson +art +ashen +ashes +atlas +atom +attic +audio +avert +avoid +awake +award +awoke +axis +bacon +badge +bagel +baggy +baked +baker +balmy +banjo +barge +barn +bash +basil +bask +batch +bath +baton +bats +blade +blank +blast +blaze +bleak +blend +bless +blimp +blink +bloat +blob +blog +blot +blunt +blurt +blush +boast +boat +body +boil +bok +bolt +boned +boney +bonus +bony +book +booth +boots +boss +botch +both +boxer +breed +bribe +brick +bride +brim +bring +brink +brisk +broad +broil +broke +brook +broom +brush +buck +bud +buggy +bulge +bulk +bully +bunch +bunny +bunt +bush +bust +busy +buzz +cable +cache +cadet +cage +cake +calm +cameo +canal +candy +cane +canon +cape +card +cargo +carol +carry +carve +case +cash +cause +cedar +chain +chair +chant +chaos +charm +chase +cheek +cheer +chef +chess +chest +chew +chief +chili +chill +chip +chomp +chop +chow +chuck +chump +chunk +churn +chute +cider +cinch +city +civic +civil +clad +claim +clamp +clap +clash +clasp +class +claw +clay +clean +clear +cleat +cleft +clerk +click +cling +clink +clip +cloak +clock +clone +cloth +cloud +clump +coach +coast +coat +cod +coil +coke +cola +cold +colt +coma +come +comic +comma +cone +cope +copy +coral +cork +cost +cot +couch +cough +cover +cozy +craft +cramp +crane +crank +crate +crave +crawl +crazy +creme +crepe +crept +crib +cried +crisp +crook +crop +cross +crowd +crown +crumb +crush +crust +cub +cult +cupid +cure +curl +curry +curse +curve +curvy +cushy +cut +cycle +dab +dad +daily +dairy +daisy +dance +dandy +darn +dart +dash +data +date +dawn +deaf +deal +dean +debit +debt +debug +decaf +decal +decay +deck +decor +decoy +deed +delay +denim +dense +dent +depth +derby +desk +dial +diary +dice +dig +dill +dime +dimly +diner +dingy +disco +dish +disk +ditch +ditzy +dizzy +dock +dodge +doing +doll +dome +donor +donut +dose +dot +dove +down +dowry +doze +drab +drama +drank +draw +dress +dried +drift +drill +drive +drone +droop +drove +drown +drum +dry +duck +duct +dude +dug +duke +duo +dusk +dust +duty +dwarf +dwell +eagle +early +earth +easel +east +eaten +eats +ebay +ebony +ebook +echo +edge +eel +eject +elbow +elder +elf +elk +elm +elope +elude +elves +email +emit +empty +emu +enter +entry +envoy +equal +erase +error +erupt +essay +etch +evade +even +evict +evil +evoke +exact +exit +fable +faced +fact +fade +fall +false +fancy +fang +fax +feast +feed +femur +fence +fend +ferry +fetal +fetch +fever +fiber +fifth +fifty +film +filth +final +finch +fit +five +flag +flaky +flame +flap +flask +fled +flick +fling +flint +flip +flirt +float +flock +flop +floss +flyer +foam +foe +fog +foil +folic +folk +food +fool +found +fox +foyer +frail +frame +fray +fresh +fried +frill +frisk +from +front +frost +froth +frown +froze +fruit +gag +gains +gala +game +gap +gas +gave +gear +gecko +geek +gem +genre +gift +gig +gills +given +giver +glad +glass +glide +gloss +glove +glow +glue +goal +going +golf +gong +good +gooey +goofy +gore +gown +grab +grain +grant +grape +graph +grasp +grass +grave +gravy +gray +green +greet +grew +grid +grief +grill +grip +grit +groom +grope +growl +grub +grunt +guide +gulf +gulp +gummy +guru +gush +gut +guy +habit +half +halo +halt +happy +harm +hash +hasty +hatch +hate +haven +hazel +hazy +heap +heat +heave +hedge +hefty +help +herbs +hers +hub +hug +hula +hull +human +humid +hump +hung +hunk +hunt +hurry +hurt +hush +hut +ice +icing +icon +icy +igloo +image +ion +iron +islam +issue +item +ivory +ivy +jab +jam +jaws +jazz +jeep +jelly +jet +jiffy +job +jog +jolly +jolt +jot +joy +judge +juice +juicy +july +jumbo +jump +junky +juror +jury +keep +keg +kept +kick +kilt +king +kite +kitty +kiwi +knee +knelt +koala +kung +ladle +lady +lair +lake +lance +land +lapel +large +lash +lasso +last +latch +late +lazy +left +legal +lemon +lend +lens +lent +level +lever +lid +life +lift +lilac +lily +limb +limes +line +lint +lion +lip +list +lived +liver +lunar +lunch +lung +lurch +lure +lurk +lying +lyric +mace +maker +malt +mama +mango +manor +many +map +march +mardi +marry +mash +match +mate +math +moan +mocha +moist +mold +mom +moody +mop +morse +most +motor +motto +mount +mouse +mousy +mouth +move +movie +mower +mud +mug +mulch +mule +mull +mumbo +mummy +mural +muse +music +musky +mute +nacho +nag +nail +name +nanny +nap +navy +near +neat +neon +nerd +nest +net +next +niece +ninth +nutty +oak +oasis +oat +ocean +oil +old +olive +omen +onion +only +ooze +opal +open +opera +opt +otter +ouch +ounce +outer +oval +oven +owl +ozone +pace +pagan +pager +palm +panda +panic +pants +panty +paper +park +party +pasta +patch +path +patio +payer +pecan +penny +pep +perch +perky +perm +pest +petal +petri +petty +photo +plank +plant +plaza +plead +plot +plow +pluck +plug +plus +poach +pod +poem +poet +pogo +point +poise +poker +polar +polio +polka +polo +pond +pony +poppy +pork +poser +pouch +pound +pout +power +prank +press +print +prior +prism +prize +probe +prong +proof +props +prude +prune +pry +pug +pull +pulp +pulse +puma +punch +punk +pupil +puppy +purr +purse +push +putt +quack +quake +query +quiet +quill +quilt +quit +quota +quote +rabid +race +rack +radar +radio +raft +rage +raid +rail +rake +rally +ramp +ranch +range +rank +rant +rash +raven +reach +react +ream +rebel +recap +relax +relay +relic +remix +repay +repel +reply +rerun +reset +rhyme +rice +rich +ride +rigid +rigor +rinse +riot +ripen +rise +risk +ritzy +rival +river +roast +robe +robin +rock +rogue +roman +romp +rope +rover +royal +ruby +rug +ruin +rule +runny +rush +rust +rut +sadly +sage +said +saint +salad +salon +salsa +salt +same +sandy +santa +satin +sauna +saved +savor +sax +say +scale +scam +scan +scare +scarf +scary +scoff +scold +scoop +scoot +scope +score +scorn +scout +scowl +scrap +scrub +scuba +scuff +sect +sedan +self +send +sepia +serve +set +seven +shack +shade +shady +shaft +shaky +sham +shape +share +sharp +shed +sheep +sheet +shelf +shell +shine +shiny +ship +shirt +shock +shop +shore +shout +shove +shown +showy +shred +shrug +shun +shush +shut +shy +sift +silk +silly +silo +sip +siren +sixth +size +skate +skew +skid +skier +skies +skip +skirt +skit +sky +slab +slack +slain +slam +slang +slash +slate +slaw +sled +sleek +sleep +sleet +slept +slice +slick +slimy +sling +slip +slit +slob +slot +slug +slum +slurp +slush +small +smash +smell +smile +smirk +smog +snack +snap +snare +snarl +sneak +sneer +sniff +snore +snort +snout +snowy +snub +snuff +speak +speed +spend +spent +spew +spied +spill +spiny +spoil +spoke +spoof +spool +spoon +sport +spot +spout +spray +spree +spur +squad +squat +squid +stack +staff +stage +stain +stall +stamp +stand +stank +stark +start +stash +state +stays +steam +steep +stem +step +stew +stick +sting +stir +stock +stole +stomp +stony +stood +stool +stoop +stop +storm +stout +stove +straw +stray +strut +stuck +stud +stuff +stump +stung +stunt +suds +sugar +sulk +surf +sushi +swab +swan +swarm +sway +swear +sweat +sweep +swell +swept +swim +swing +swipe +swirl +swoop +swore +syrup +tacky +taco +tag +take +tall +talon +tamer +tank +taper +taps +tarot +tart +task +taste +tasty +taunt +thank +thaw +theft +theme +thigh +thing +think +thong +thorn +those +throb +thud +thumb +thump +thus +tiara +tidal +tidy +tiger +tile +tilt +tint +tiny +trace +track +trade +train +trait +trap +trash +tray +treat +tree +trek +trend +trial +tribe +trick +trio +trout +truce +truck +trump +trunk +try +tug +tulip +tummy +turf +tusk +tutor +tutu +tux +tweak +tweet +twice +twine +twins +twirl +twist +uncle +uncut +undo +unify +union +unit +untie +upon +upper +urban +used +user +usher +utter +value +vapor +vegan +venue +verse +vest +veto +vice +video +view +viral +virus +visa +visor +vixen +vocal +voice +void +volt +voter +vowel +wad +wafer +wager +wages +wagon +wake +walk +wand +wasp +watch +water +wavy +wheat +whiff +whole +whoop +wick +widen +widow +width +wife +wifi +wilt +wimp +wind +wing +wink +wipe +wired +wiry +wise +wish +wispy +wok +wolf +womb +wool +woozy +word +work +worry +wound +woven +wrath +wreck +wrist +xerox +yahoo +yam +yard +year +yeast +yelp +yield +yo-yo +yodel +yoga +yoyo +yummy +zebra +zero +zesty +zippy +zone +zoom diff --git a/functions/__jobrunner_sessions.fish b/functions/__jobrunner_sessions.fish index 97c5de6..735a7a3 100644 --- a/functions/__jobrunner_sessions.fish +++ b/functions/__jobrunner_sessions.fish @@ -2,42 +2,63 @@ # SPDX-License-Identifier: AGPL-3.0-or-later # SYNOPSIS -# __jobrunner_sessions +# __jobrunner_sessions [] # # DESCRIPTION -# Parses `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. +# 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 screen is not installed +# 1 tool (tmux or screen) is not installed # # RETURNS # One line per session: \t\t\t # # EXAMPLE # __jobrunner_sessions -# __jobrunner_sessions | string replace -r '\t.*$' '' -function __jobrunner_sessions --description 'List active screen sessions as name/pid/state/started rows' - 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 ".". - set -l m (string match -r '^\s+(\d+)\.([^\t]+)\t(.*)$' -- $line) - or continue - - # Trailing fields are parenthesized: "()\t()", 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]) +# __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 - printf '%s\t%s\t%s\t%s\n' $m[3] $m[2] $state $started + 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 ".". + set -l m (string match -r '^\s+(\d+)\.([^\t]+)\t(.*)$' -- $line) + or continue + + # Trailing fields are parenthesized: "()\t()", 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 diff --git a/functions/__rand_string.fish b/functions/__rand_string.fish new file mode 100644 index 0000000..672dc5b --- /dev/null +++ b/functions/__rand_string.fish @@ -0,0 +1,150 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# 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: +# A bundled word list (e.g. adjective, animal, color, name, noun) +# digits= N random digits (e.g. digits=3 -> 842) +# literal= A literal string component +# +# ARGUMENTS +# -s, --separator= Delimiter for subsequent words (dash, underscore, dot, none, or literal chars) +# -c, --case= Casing for subsequent words (lower, upper, title) +# +# 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 +# +# 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 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 + 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 + + # 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 diff --git a/functions/jobrunner.fish b/functions/jobrunner.fish index b8ed995..69e0734 100644 --- a/functions/jobrunner.fish +++ b/functions/jobrunner.fish @@ -5,24 +5,25 @@ # 08-terminal-management # # DEPENDENCIES -# screen, __jobrunner_sessions +# tmux, screen, __jobrunner_sessions # # SYNOPSIS -# jobrunner [] [] [...] -# jr [] [] [...] +# jobrunner [-t ] [] [] [...] +# jr [-t ] [] [] [...] # # DESCRIPTION # 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. +# 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. # # 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 +# -t, --tool Force specific backend (tmux or screen) # run, -r, --run ... Start a named job in the background # list, -l, --list List all managed background jobs # attach, -a, --attach Re-attach interactively to a job @@ -33,11 +34,11 @@ # 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 +# 127 neither tmux nor screen is installed # # EXAMPLE # jobrunner run build make -j8 -# jobrunner backup rsync -a ./data remote:/backup/ +# jobrunner -t screen run backup rsync -a ./data remote:/backup/ # jobrunner list # jobrunner logs build # jobrunner build @@ -48,7 +49,7 @@ # 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 GNU screen' +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) @@ -61,6 +62,29 @@ function jobrunner --description 'Manage detached background jobs with GNU scree 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 "" + 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 '*' + break + end + end + # ╭──────────────────────────────────────────────────────────╮ # │ Help │ # ╰──────────────────────────────────────────────────────────╯ @@ -68,7 +92,7 @@ function jobrunner --description 'Manage detached background jobs with GNU scree 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""[] [] [...]$c_rst" echo - echo " Run and manage named background jobs backed by GNU screen." + echo " Run and manage named background jobs backed by tmux or GNU screen." echo echo "$c_head""Subcommands:$c_rst" echo " $c_cmd""run$c_rst $c_arg"" ...$c_rst Start a named job in the background" @@ -78,6 +102,7 @@ function jobrunner --description 'Manage detached background jobs with GNU scree echo " $c_cmd""logs$c_rst $c_arg""$c_rst Print a job's output without attaching" echo echo "$c_head""Flags:$c_rst" + echo " $c_flag-t$c_rst, $c_flag--tool$c_rst $c_arg$c_rst Force specific backend (tmux or screen)" echo " $c_flag-r$c_rst, $c_flag--run$c_rst Same as $c_cmd""run$c_rst" echo " $c_flag-l$c_rst, $c_flag--list$c_rst Same as $c_cmd""list$c_rst" echo " $c_flag-a$c_rst, $c_flag--attach$c_rst Same as $c_cmd""attach$c_rst" @@ -102,9 +127,24 @@ function jobrunner --description 'Manage detached background jobs with GNU scree # ╭──────────────────────────────────────────────────────────╮ # │ Dependency check │ # ╰──────────────────────────────────────────────────────────╯ - if not command -q screen - echo "$c_err""jobrunner:$c_rst 'screen' is required but was not found in PATH." >&2 - return 127 + 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 # ╭──────────────────────────────────────────────────────────╮ @@ -116,7 +156,7 @@ function jobrunner --description 'Manage detached background jobs with GNU scree 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 | string replace -r '\t.*$' '') + if contains -- $cmd (__jobrunner_sessions $tool | string replace -r '\t.*$' '') set argv attach $argv set cmd attach else @@ -148,19 +188,23 @@ function jobrunner --description 'Manage detached background jobs with GNU scree echo "$c_err""jobrunner:$c_rst job name may not contain '/'." >&2 return 1 end - if contains -- $name (__jobrunner_sessions | string replace -r '\t.*$' '') + 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 - command screen -d -m -S $name $task + 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) + 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 @@ -173,7 +217,7 @@ function jobrunner --description 'Manage detached background jobs with GNU scree end case list -l --list - set -l rows (__jobrunner_sessions) + set -l rows (__jobrunner_sessions $tool) if test (count $rows) -eq 0 echo "No background jobs running." return 0 @@ -192,12 +236,16 @@ function jobrunner --description 'Manage detached background jobs with GNU scree return 1 end set -l name $argv[2] - if not contains -- $name (__jobrunner_sessions | string replace -r '\t.*$' '') + 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 - # -x attaches to an already-attached session instead of failing. - command screen -x $name + 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 @@ -205,11 +253,15 @@ function jobrunner --description 'Manage detached background jobs with GNU scree return 1 end set -l name $argv[2] - if not contains -- $name (__jobrunner_sessions | string replace -r '\t.*$' '') + 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 - command screen -X -S $name quit + 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 @@ -222,25 +274,36 @@ function jobrunner --description 'Manage detached background jobs with GNU scree return 1 end set -l name $argv[2] - if not contains -- $name (__jobrunner_sessions | string replace -r '\t.*$' '') + 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 - # 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 + 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 - echo "$c_err""jobrunner:$c_rst could not read output of '$c_arg$name$c_rst'." >&2 - return 1 end - set -l out (command cat $dump) - command rm -f $dump - # hardcopy pads the dump out to the full window height. + # 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 diff --git a/functions/lstree.fish b/functions/lstree.fish index 5948c90..49993ea 100644 --- a/functions/lstree.fish +++ b/functions/lstree.fish @@ -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