Import from root chezmoi source

This commit is contained in:
Prad Nukala committed 2026-10-14 13:32:31 -04:00
commit 3eeef30313
61 files changed
+2833

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
function _gho_bind --description 'ctrl-o: gho (tv --inline), then redraw the prompt in the right place'
set -l before $PWD
gho
if test "$PWD" = "$before"
# Cancelled (or the nvim action ran): tv --inline left the cursor on
# the line below the prompt; step back so repaint redraws in place.
printf '\033[A' >/dev/tty
else
# cd + clear + onefetch: put the prompt below onefetch's output.
printf '\n' >/dev/tty
end
commandline -f repaint
end
+54
View File
@@ -0,0 +1,54 @@
function _jobs_bind --description 'ctrl-z: fg the only job, or pick one from the jobs tv cable'
# Only reached at the prompt: while a job owns the terminal, ctrl-z is the
# tty's SIGTSTP and stops it. So ctrl-z toggles: suspend, then resume.
# Records for the `jobs` cable: display, job id, process group (tabs).
set -l recs
for line in (jobs 2>/dev/null)
set -l f (string split \t -- $line)
string match -qr '^\d+$' -- $f[1]; or continue # header row
set -a recs (printf '%%%s %-8s %s\t%s\t%s' $f[1] $f[4] $f[5] $f[1] $f[2])
end
set -l id
switch (count $recs)
case 0
return
case 1
set id (string split \t -- $recs[1])[2]
case '*'
set -lx TV_JOBS (mktemp)
printf '%s\n' $recs >$TV_JOBS
set id (_tv_inline jobs)
rm -f $TV_JOBS
end
if test -z "$id"
commandline -f repaint
return
end
# Run fg as a command line, not from the binding: a binding runs with the
# terminal still in fish's reader mode (extended keys on), so a job that
# never had the terminal (started with &) would get ctrl-z/ctrl-c as escape
# sequences instead of signals. The leading space keeps it out of history.
# Whatever was typed comes back at the next prompt.
set -l buf (commandline | string collect)
if test -n "$buf"
set -g __jobs_bind_buf $buf
function __jobs_bind_restore --on-event fish_prompt
functions -e __jobs_bind_restore
commandline -r -- $__jobs_bind_buf
set -e __jobs_bind_buf
end
end
# Executing leaves "❯ __jobs_bind_fg 1" on the prompt line: step up and
# erase it, and drop fg's "Send job … to foreground" (stderr of fg only;
# the job keeps its own stdout/stderr).
function __jobs_bind_fg
printf '\033[A\033[2K\r' >/dev/tty
fg %$argv[1] 2>/dev/null
end
commandline -r -- " __jobs_bind_fg $id"
commandline -f execute
end
+20
View File
@@ -0,0 +1,20 @@
function _nvm_index_update
test ! -d $nvm_data && command mkdir -p $nvm_data
set --local index $nvm_data/.index
if not command curl -q --location --silent $nvm_mirror/index.tab >$index.temp
command rm -f $index.temp
echo "nvm: Can't update index, host unavailable: \"$nvm_mirror\"" >&2
return 1
end
command awk -v OFS=\t '
/v0.9.12/ { exit } # Unsupported
NR > 1 {
print $1 (NR == 2 ? " latest" : $10 != "-" ? " lts/" tolower($10) : "")
}
' $index.temp >$index
command rm -f $index.temp
end
+14
View File
@@ -0,0 +1,14 @@
function _nvm_list
set --local versions $nvm_data/*
set --query versions[1] &&
string match --entire --regex -- (
string replace --all -- $nvm_data/ "" $versions |
string match --regex -- "v\d.+" |
string escape --style=regex |
string join "|"
) <$nvm_data/.index
command --all node |
string match --quiet --invert --regex -- "^$nvm_data" && echo system
end
+4
View File
@@ -0,0 +1,4 @@
function _nvm_version_activate --argument-names ver
set --global --export nvm_current_version $ver
set --prepend PATH $nvm_data/$ver/bin
end
+5
View File
@@ -0,0 +1,5 @@
function _nvm_version_deactivate --argument-names ver
test "$nvm_current_version" = "$ver" && set --erase nvm_current_version
set --local index (contains --index -- $nvm_data/$ver/bin $PATH) &&
set --erase PATH[$index]
end
+21
View File
@@ -0,0 +1,21 @@
function _pj_bind --description 'ctrl-p / down: pj (tv --inline), then redraw the prompt in the right place'
# pj reports through $PJ_STATE: `open` = the picker was cancelled and
# left the cursor on the line below the prompt; empty = no task sources,
# nothing drawn — but fish's repaint after an external command still
# lands one line low, so both step back up. `ran` = a task printed
# output: new line, prompt below it.
set -lx PJ_STATE (mktemp)
if contains -- --quiet $argv
set -lx PJ_QUIET 1
end
pj
set -l state (cat $PJ_STATE)
switch "$state"
case open ''
printf '\033[A' >/dev/tty
case ran
printf '\n' >/dev/tty
end
rm -f $PJ_STATE
commandline -f repaint
end
+21
View File
@@ -0,0 +1,21 @@
function _pj_bind_down --description "down arrow: pj tv cable on an idle empty prompt, else fish's down-or-search"
# Mirror of atuin's up binding (atuin owns up, this owns down). Everything
# that legitimately needs the arrow keys keeps them:
# - non-empty buffer → multiline cursor movement / forward history search
# - paging mode → tab-completion pager navigation (tab on an empty
# line opens the pager with an empty buffer)
# - search mode → history-search navigation
# down-or-search is fish's own input function, called as a command exactly
# the way atuin's _atuin_bind_up calls up-or-search.
set -l buf (commandline | string collect)
if test -n "$buf"; or commandline --paging-mode; or commandline --search-mode
down-or-search
return
end
# _pj_bind runs ~/.local/bin/pj (also ctrl-p), which opens the `pj` tv
# cable. --quiet (PJ_QUIET) keeps it silent when the tree has no task
# source at all, so down is a no-op outside projects instead of printing
# an error per press.
_pj_bind --quiet
end
+10
View File
@@ -0,0 +1,10 @@
function _tv_inline --description 'tv --inline below the prompt, cursor back on the prompt line; stdout is the pick'
# tv --inline opens on the line below the prompt and leaves the cursor on
# its own first row when it exits; step back up so the following
# `commandline -f repaint` redraws the prompt in place instead of one line
# lower. /dev/tty keeps the move out of callers that capture stdout (gho).
tv $argv --inline
set -l st $status
printf '\033[A' >/dev/tty
return $st
end
+15
View File
@@ -0,0 +1,15 @@
function ai --description 'Choose and launch an AI CLI via gum'
set -l choice (gum choose --header 'AI CLI' claude opencode codex hermes fractal)
test -n "$choice"; or return # cancelled (Esc / Ctrl-C) → bail out
switch $choice
case claude
claude --dangerously-skip-permissions
case '*'
# labels match the binary names, so just run it
# add a dedicated `case` here if another tool needs flags
$choice
end
end
+9
View File
@@ -0,0 +1,9 @@
function c
if test -z "$argv[1]"
clear
else
cd $argv[1]
end
end
+3
View File
@@ -0,0 +1,3 @@
function c2p --description "Code2Prompt with fuzzy-search template"
fd --type f --extension hbs . $HOME/.config/code2prompt | fzf | xargs code2prompt --template
end
+20
View File
@@ -0,0 +1,20 @@
function cd --description 'cd with zoxide fallback, else builtin cd'
if test (count $argv) -eq 0
builtin cd $HOME
else if test "$argv[1]" = '-'
builtin cd -
else if builtin cd $argv[1] 2>/dev/null
# exact path resolved, nothing else to do
else if type -q zoxide
set -l target (zoxide query -- $argv[1] 2>/dev/null)
if test -n "$target"
builtin cd $target
else
builtin cd $argv[1] # let builtin cd surface the real error
end
else
builtin cd $argv[1]
end
type -q zoxide; and zoxide add $PWD
end
+11
View File
@@ -0,0 +1,11 @@
function clear --wraps clear --description 'clear screen + kitty scrollback, prompt pinned to the bottom'
if not status is-interactive
command clear $argv
return
end
# kitty doesn't clear scrollback with plain `clear`; 3J drops it.
printf '\033[2J\033[3J\033[1;1H'
# Re-pin only when the bottom prompt is enabled (FISH_BOTTOM_PROMPT=0
# keeps the default top prompt).
test "$FISH_BOTTOM_PROMPT" = 1; and tput cup $LINES 0
end
+4
View File
@@ -0,0 +1,4 @@
function clip-last --description 'Re-run last command, copy output to clipboard'
eval $history[1] | cpy
notify-send -a fish "Clipboard" "Command output copied to clipboard"
end
+43
View File
@@ -0,0 +1,43 @@
function cp-appid --description "Fuzzy-pick a desktop app ID and copy to clipboard"
set -l search_dirs \
/usr/share/applications \
/usr/local/share/applications \
$HOME/.local/share/applications \
/var/lib/flatpak/exports/share/applications \
$HOME/.local/share/flatpak/exports/share/applications \
/var/lib/snapd/desktop/applications
set -l existing_dirs
for d in $search_dirs
test -d $d; and set -a existing_dirs $d
end
test (count $existing_dirs) -eq 0
and echo "No application directories found." >&2
and return 1
set -l chosen (
fd --type f --extension desktop . $existing_dirs \
| while read -l f
set -l app_id (basename $f .desktop)
set -l name (rg -m1 '^Name=' $f | string replace 'Name=' '')
test -z "$name"; and set name $app_id
echo "$name\t$app_id"
end \
| sort -u -t\t -k1 \
| fzf \
--delimiter='\t' \
--with-nth=1,2 \
--prompt="App ID › " \
--height=50% \
--layout=reverse \
--border=rounded \
--info=inline
)
test -z "$chosen"; and return 0
set -l app_id (string split '\t' $chosen)[2]
echo -n $app_id | wl-copy
echo "Copied: $app_id"
end
+37
View File
@@ -0,0 +1,37 @@
function czs --description 'chezmoi: re-add drifted files, then commit + push the source repo'
# Optional targets scope both the status view and the re-add,
# e.g. `chs ~/.config/fish`. No args = all managed files.
set -l drift (chezmoi status $argv)
or return
if test -n "$drift"
printf '%s\n' $drift
# re-add takes the local (destination) contents as truth for modified
# files; templates are never overwritten, non-file entries are ignored.
chezmoi re-add $argv
or return
else
echo "chs: in sync — nothing to re-add"
end
# Commit + sync the source repo (~/.local/share/chezmoi).
set -l src (chezmoi source-path)
if test -n "$(git -C $src status --porcelain)"
git -C $src add -A
and git -C $src commit --quiet -m 'chs: re-add drifted files'
or return
echo "chs: committed "(git -C $src rev-parse --short HEAD)
end
# Push anything unpushed — covers this commit AND leftovers from a
# previously failed push. --autostash tolerates post-commit hooks
# (beads export) re-dirtying the tree mid-sync.
set -l ahead (git -C $src rev-list --count @{upstream}..HEAD 2>/dev/null; or echo 0)
if test $ahead -gt 0
git -C $src pull --rebase --autostash --quiet
and git -C $src push --quiet
and echo "chs: pushed $ahead commit(s) to "(git -C $src remote get-url origin)
else
echo "chs: remote up to date"
end
end
+18
View File
@@ -0,0 +1,18 @@
function extract_agents -d "Copy all AGENTS.md files to a target directory while preserving structure"
set -l out_dir $argv[1]
# Validate input
if test -z "$out_dir"
echo "Error: Output directory required."
echo "Usage: extract_agents <absolute_or_relative_output_path>"
return 1
end
# Ensure output directory exists
mkdir -p $out_dir
# Find and copy preserving relative paths
fd -t f -g "AGENTS.md" -x cp --parents "{}" $out_dir
echo "Extraction complete. Check $out_dir."
end
+40
View File
@@ -0,0 +1,40 @@
function fab -d "Fuzzy find and execute a Fabric pattern"
set -l json_file ~/.config/fabric/pattern_explainations.json
if not test -f $json_file
set_color red; echo "Error: $json_file not found."; set_color normal
return 1
end
# 1. Safely capture piped input (if any) to a temp file
# This preserves all formatting, newlines, and avoids variable escaping issues
set -l temp_input (mktemp)
if not isatty stdin
cat > $temp_input
end
# 2. Parse JSON to TSV -> fzf (Name in list, Description in preview) -> Extract Name
set -l selection (jq -r '.[] | "\(.name)\t\(.description)"' $json_file | \
fzf --delimiter '\t' \
--with-nth 1 \
--preview 'echo {2}' \
--preview-window 'right:60%:wrap' \
--prompt 'Pattern > ' \
--info inline \
--bind 'ctrl-/:toggle-preview')
# 3. Execute with or without the piped data
if test -n "$selection"
set -l pattern (string split -f 1 \t "$selection")
# If our temp file has data (-s), pipe it. Otherwise, run bare.
if test -s $temp_input
cat $temp_input | fabric --pattern "$pattern"
else
fabric --pattern "$pattern"
end
end
# 4. Clean up
rm -f $temp_input
end
+8
View File
@@ -0,0 +1,8 @@
function fish_title
# Show a short hostname over SSH, otherwise just the current directory.
if set -q SSH_TTY
echo (prompt_hostname | string sub -l 10):(prompt_pwd)
else
prompt_pwd
end
end
+50
View File
@@ -0,0 +1,50 @@
function fish_user_key_bindings
# Vi mode first: it rebinds the preset tables and (on first activation)
# re-triggers this function via the fish_key_bindings handler, so custom
# binds must come after it to land on top.
fish_vi_key_bindings
# Vi mode types in `insert`; bind customs there AND in `default` (normal)
# so the keys work regardless of mode.
for mode in insert default
# Keys owned by kitty.conf never reach fish and are NOT bound here:
# ctrl-g → lazygit overlay, ctrl-y → yazi vsplit,
# ctrl-h/j/k/l → vim-aware split nav (pass_keys.py).
# ctrl-e: tv git-files (bat preview, enter opens nvim). ctrl-o, ctrl-p
# and down also open tv cables — same keys in zsh and bash. tv runs
# --inline below the prompt; _tv_inline/_gho_bind/_pj_bind put the
# prompt back in place (or below any output) afterwards. ctrl-a:
# omp-session resumes or starts omp for the repo (same in zsh); when it
# opens the omp-sessions cable and it is cancelled, it steps back up.
# ctrl-n: always a new omp session.
bind -M $mode ctrl-e '_tv_inline git-files; commandline -f repaint'
bind -M $mode ctrl-b purple repaint
bind -M $mode ctrl-y clip-last
bind -M $mode ctrl-o _gho_bind
bind -M $mode ctrl-p _pj_bind
bind -M $mode ctrl-a 'omp-session; commandline -f repaint'
bind -M $mode ctrl-n 'omp; commandline -f repaint'
bind -M $mode ctrl-w 'atuin ai inline; commandline -f repaint'
# ctrl-z at the prompt: fg the only background job, or pick one from
# the jobs tv cable when there are several (_jobs_bind).
bind -M $mode ctrl-z _jobs_bind
# Arrow keys: atuin owns up (bound from config.fish, survives the
# fish_vi_key_bindings reset above because that only erases presets),
# down opens the pj tv cable — but only on an idle, empty prompt; see
# _pj_bind_down.
bind -M $mode down _pj_bind_down
# ctrl-f: yazi (cd's to yazi's exit dir on quit).
bind -M $mode ctrl-f 'yy; commandline -f repaint'
bind -M $mode ctrl-g 'lazygit; commandline -f repaint'
# ctrl-c routes through the clear wrapper (kitty scrollback + prompt
# pinned to the bottom) AND wipes any text typed on the command line.
bind -M $mode ctrl-c 'commandline -r ""; clear; commandline -f repaint'
# ctrl-q mirrors ctrl-d's preset (`exit`) — quit the shell.
bind -M $mode ctrl-q exit
end
end
+9
View File
@@ -0,0 +1,9 @@
function gdc --description "Discard all git changes with gum confirmation"
if gum confirm "Discard all uncommitted changes? This is irreversible."
git reset --hard HEAD
git clean -fd
echo "Changes wiped."
else
echo "Operation aborted."
end
end
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/env fish
# sonr-consolidate.fish
# Migrates all repos from orgs matching "sonr" into sonr-io,
# prompting via gum for renames on collision, then deletes emptied orgs.
#
# Requires: gh (authenticated as org owner on all orgs), gum, jq
set -g TARGET_ORG sonr-io
# ── Preflight ────────────────────────────────────────────────────────────────
for bin in gh gum jq
if not command -q $bin
echo (set_color red)"missing dependency: $bin"(set_color normal)
exit 1
end
end
if not gh auth status &>/dev/null
echo (set_color red)"gh is not authenticated"(set_color normal)
exit 1
end
# ── Discover source orgs ─────────────────────────────────────────────────────
# All orgs the user belongs to whose login contains "sonr", excluding target.
set -l source_orgs (gh api user/orgs --paginate --jq '.[].login' \
| string match -r '.*sonr.*' \
| string match -v $TARGET_ORG)
if test (count $source_orgs) -eq 0
echo "No source orgs found. Nothing to do."
exit 0
end
echo (set_color cyan)"Source orgs:"(set_color normal)
printf ' %s\n' $source_orgs
echo (set_color cyan)"Target org:"(set_color normal)" $TARGET_ORG"
echo
# Let the user confirm / trim the list interactively
set -l selected_orgs (printf '%s\n' $source_orgs | gum choose --no-limit \
--header "Select orgs to migrate into $TARGET_ORG (space to toggle, enter to confirm)")
if test (count $selected_orgs) -eq 0
echo "Nothing selected. Exiting."
exit 0
end
# ── Helpers ──────────────────────────────────────────────────────────────────
function repo_exists_in_target
gh api "repos/$TARGET_ORG/$argv[1]" --silent &>/dev/null
end
function transfer_repo
# $1 = source org, $2 = repo name, $3 = (optional) new name
set -l src $argv[1]
set -l repo $argv[2]
set -l args -f new_owner=$TARGET_ORG
if set -q argv[3]; and test -n "$argv[3]"
set -a args -f new_name=$argv[3]
end
gh api -X POST "repos/$src/$repo/transfer" $args --silent
end
# ── Migration ────────────────────────────────────────────────────────────────
set -g failed_transfers
for org in $selected_orgs
echo
echo (set_color --bold cyan)"── $org ──"(set_color normal)
set -l repos (gh api "orgs/$org/repos" --paginate --jq '.[].name')
if test (count $repos) -eq 0
echo " (no repos)"
continue
end
for repo in $repos
set -l new_name ""
if repo_exists_in_target $repo
echo (set_color yellow)" ⚠ $repo already exists in $TARGET_ORG"(set_color normal)
while true
set new_name (gum input \
--header "New name for $org/$repo (exists in $TARGET_ORG)" \
--placeholder "$org-$repo" \
--value "$org-$repo")
if test -z "$new_name"
echo " skipped $org/$repo"
set new_name __SKIP__
break
end
if repo_exists_in_target $new_name
echo (set_color yellow)" $new_name also exists — pick another"(set_color normal)
else
break
end
end
test "$new_name" = __SKIP__; and continue
end
set -l label "$org/$repo → $TARGET_ORG/"(test -n "$new_name"; and echo $new_name; or echo $repo)
if transfer_repo $org $repo $new_name
echo (set_color green)" ✓ $label"(set_color normal)
else
echo (set_color red)" ✗ failed: $label"(set_color normal)
set -a failed_transfers "$org/$repo"
end
end
end
if test (count $failed_transfers) -gt 0
echo
echo (set_color red)"Failed transfers (orgs containing these will NOT be deleted):"(set_color normal)
printf ' %s\n' $failed_transfers
end
# ── Wait for transfers to settle ─────────────────────────────────────────────
# GitHub repo transfers are asynchronous; give them time to drain.
echo
gum spin --title "Waiting 30s for transfers to settle..." -- sleep 30
# ── Delete emptied orgs ──────────────────────────────────────────────────────
echo
for org in $selected_orgs
# Skip orgs with failed transfers
if string match -q "$org/*" -- $failed_transfers
echo (set_color yellow)"skipping delete: $org (had failures)"(set_color normal)
continue
end
set -l remaining (gh api "orgs/$org/repos" --paginate --jq '. | length' | jq -s 'add // 0')
if test "$remaining" -gt 0
echo (set_color yellow)"skipping delete: $org still has $remaining repo(s) (transfer pending?)"(set_color normal)
continue
end
if gum confirm "Delete empty org $org?"
if gh api -X DELETE "orgs/$org" --silent
echo (set_color green)"✓ deleted $org"(set_color normal)
else
echo (set_color red)"✗ could not delete $org (API delete requires org owner; may need manual deletion in Settings)"(set_color normal)
end
else
echo "kept $org"
end
end
echo
echo (set_color --bold green)"Done."(set_color normal)
+150
View File
@@ -0,0 +1,150 @@
function gh-wiki --description "Clone a GitHub wiki and build one PDF from its Markdown pages"
# Usage: gh-wiki <WIKI URL> [-o OUTPUT.pdf] [-t TEMPLATE] [-d] [-k]
argparse 'h/help' 'o/output=' 't/template=' 'd/docker' 'k/keep' -- $argv
or return 1
if set -q _flag_help; or test (count $argv) -eq 0
echo "Usage: gh-wiki <WIKI URL> [-o OUTPUT.pdf] [-t TEMPLATE] [-d] [-k]"
echo " -o Output PDF path (default: <repo>-wiki.pdf in the current directory)"
echo " -t Pandoc template (default: eisvogel, from ~/.local/share/pandoc/templates)"
echo " -d Use the pandoc/extra Docker image instead of the local pandoc"
echo " -k Keep the clone in /tmp"
return 0
end
# --- Check dependencies ---------------------------------------------
set -l deps git gum
set -l engine
if set -q _flag_docker
set -a deps docker
else
set -a deps pandoc
# Eisvogel needs a XeTeX engine; tectonic is a self-contained one.
for e in xelatex tectonic
command -q $e; and set engine $e; and break
end
if test -z "$engine"
echo "gh-wiki: install xelatex or tectonic, or pass -d to use Docker" >&2
return 1
end
end
for bin in $deps
if not command -q $bin
echo "gh-wiki: '$bin' is not installed" >&2
return 1
end
end
set -l url $argv[1]
# Accept a plain repo URL and turn it into the wiki URL.
if not string match -q '*.wiki.git' $url
set url (string replace -r '(\.git)?/?$' '.wiki.git' $url)
end
# Repo name: https://github.com/user/repo.wiki.git -> repo
set -l repo (string replace -r '\.wiki\.git$' '' (basename $url))
set -l clone_dir (mktemp -d /tmp/gh-wiki-$repo-XXXXXX)
set -l out "$repo-wiki.pdf"
set -q _flag_output; and set out $_flag_output
# --- Clone ----------------------------------------------------------
gum spin --show-error --spinner dot --title "Cloning $url" -- \
git clone --quiet --depth 1 $url $clone_dir
or begin
gum log --level error "Clone failed: $url"
rm -rf $clone_dir
return 1
end
# --- Collect pages --------------------------------------------------
# Home first, then the rest in alphabetical order. Skip wiki chrome.
set -l pages
test -f $clone_dir/Home.md; and set -a pages $clone_dir/Home.md
for f in (find $clone_dir -maxdepth 1 -name '*.md' | sort)
set -l name (basename $f)
string match -q 'Home.md' $name; and continue
string match -q '_*' $name; and continue
set -a pages $f
end
if test (count $pages) -eq 0
gum log --level error "No Markdown pages found in $url"
rm -rf $clone_dir
return 1
end
gum log --level info "Found "(count $pages)" pages"
# --- Build one Markdown source --------------------------------------
# Each page becomes a chapter. Its own headings move one level down.
set -l src_name _combined.md
set -l src $clone_dir/$src_name
for f in $pages
set -l title (string replace -a -- '-' ' ' (basename $f .md))
printf '\n# %s\n\n' "$title" >> $src
cat $f >> $src
printf '\n\n' >> $src
end
# --- Shared pandoc options ------------------------------------------
set -l template eisvogel
set -q _flag_template; and set template $_flag_template
set -l common_args \
--from gfm \
--shift-heading-level-by=1 \
--toc --toc-depth=2 \
--number-sections \
-M title="$repo Wiki" \
-M date=(date +%Y-%m-%d) \
-V titlepage=true \
-V toc-own-page=true
# --- Render ---------------------------------------------------------
set -l status_code 0
if set -q _flag_docker
# pandoc/extra ships pandoc, LaTeX and Eisvogel. Mount the clone as
# /data, write the PDF there, then move it to the requested path.
set -l tmp_pdf _out.pdf
gum spin --show-error --spinner line --title "Rendering $out" -- \
docker run --rm \
--volume "$clone_dir:/data" \
--user (id -u):(id -g) \
pandoc/extra $src_name -o $tmp_pdf \
--resource-path /data \
--template $template --pdf-engine xelatex \
$common_args
set status_code $status
test $status_code -eq 0; and mv $clone_dir/$tmp_pdf $out
else
set -l pandoc_args --pdf-engine $engine
# Tectonic's bundle lacks Eisvogel's default sourcesans/sourcecodepro
# packages; naming system fonts makes the template skip them.
test $engine = tectonic; and set -a pandoc_args \
-V mainfont="Noto Sans" -V monofont="JetBrains Mono"
set -l data_dir (pandoc --version | string match -r 'User data directory: (.*)' | tail -n1)
if test -f "$data_dir/templates/$template.latex"; or test -f "$template"
set -a pandoc_args --template $template
else
gum log --level warn "Template '$template' not found; using the pandoc default"
end
gum spin --show-error --spinner line --title "Rendering $out" -- \
pandoc $src -o $out \
--resource-path $clone_dir \
$pandoc_args \
$common_args
set status_code $status
end
# --- Clean up -------------------------------------------------------
if set -q _flag_keep
gum log --level info "Clone kept at $clone_dir"
else
rm -rf $clone_dir
end
if test $status_code -ne 0
gum log --level error "pandoc failed"
return $status_code
end
gum log --level info "Wrote $out"
end
+5
View File
@@ -0,0 +1,5 @@
function ghg -a repo --description 'ghq get + cd (clone/path logic in ~/.local/bin/ghg-clone)'
set -l dir (ghg-clone $repo); or return
cd $dir; or return
clear; and onefetch
end
+4
View File
@@ -0,0 +1,4 @@
function ghn -a repo --description 'create repo + cd (logic in ~/.local/bin/ghn)'
set -l dir (command ghn $repo); or return
cd $dir
end
+9
View File
@@ -0,0 +1,9 @@
function gho --description 'pick a ghq repo / Obsidian vault (tv ghq-repos cable) + cd'
set -l dir (tv ghq-repos --inline); or return
test -n "$dir"; or return 1
cd $dir; or return
clear
if test -d .git
onefetch
end
end
+22
View File
@@ -0,0 +1,22 @@
function git-summary --description "Aggregate all AGENTS.md files in the current git repo via code2prompt"
# Resolve the repo root so it works from any subdirectory
set -l repo_root (git rev-parse --show-toplevel 2>/dev/null)
if test -z "$repo_root"
echo "git-summary: not inside a git repository" >&2
return 1
end
set -l template ~/.config/code2prompt/agents-md.hbs
if not test -f $template
echo "git-summary: template not found at $template" >&2
return 1
end
# Pass through any extra flags (e.g. --output-file, --tokens)
code2prompt $repo_root \
--include "AGENTS.md" \
--include "**/AGENTS.md" \
--template $template \
$argv
end
+160
View File
@@ -0,0 +1,160 @@
# ~/.config/fish/functions/gtm.fish
# Migrate GitHub repos → prad.codes (Gitea) via tea.
# Usage: gtm [<owner>/<repo> | <github-url>] — no arg: multi-select via fzf
function gtm --description "Migrate GitHub repo(s) to a prad.codes org via tea"
# -- Preflight ------------------------------------------------------------
for bin in gh tea gum jq
if not command -q $bin
echo "gtm: missing dependency: $bin" >&2
return 1
end
end
set -l gh_token (gh auth token 2>/dev/null)
if test -z "$gh_token"
echo "gtm: gh not authenticated (run: gh auth login)" >&2
return 1
end
# -- Resolve source repos -------------------------------------------------
# Each entry: nameWithOwner \t isPrivate
set -l repos
if test (count $argv) -ge 1
# Direct mode: accepts owner/repo, https://…, ssh://…, git@host:…
# Same normalization as ghg-clone: strip scheme, user@, scp colon, .git
set -l rel (
printf '%s\n' $argv[1] \
| sed -E 's#^[[:alnum:]]+://##; s#^[^@/]+@##; s#:#/#; s#\.git$##; s#/+$##'
)
# Reduce to owner/repo: take the last two path segments
set -l parts (string split / -- $rel | string match -rv '^$')
if test (count $parts) -lt 2
echo "usage: gtm [<owner>/<repo> | <github-url>]" >&2
return 1
end
# If a host segment is present, it must be github.com — that's the
# only source this migrator pulls from (clone-url is hardcoded to it)
if test (count $parts) -gt 2; and test "$parts[-3]" != github.com
echo "gtm: source must be a github.com repo, got host: $parts[-3]" >&2
return 1
end
set -l nwo "$parts[-2]/$parts[-1]"
if not string match -qr '^[\w.-]+/[\w.-]+$' -- $nwo
echo "gtm: could not parse owner/repo from: $argv[1]" >&2
return 1
end
set -l meta (
gh repo view $nwo \
--json nameWithOwner,isPrivate \
--jq '[.nameWithOwner, (.isPrivate|tostring)] | @tsv' 2>/dev/null
)
if test -z "$meta"
echo "gtm: repo not found or inaccessible: $nwo" >&2
return 1
end
set repos $meta
else
# Interactive mode: fzf multi-select
if not command -q fzf
echo "gtm: missing dependency: fzf" >&2
return 1
end
set repos (
gh repo list --limit 200 \
--json nameWithOwner,isPrivate,description \
--jq '.[] | [.nameWithOwner, (.isPrivate|tostring), .description // ""] | @tsv' \
| fzf --multi \
--delimiter '\t' \
--with-nth 1,3 \
--prompt 'migrate> ' \
--header 'TAB: multi-select · ENTER: confirm' \
--preview 'gh repo view {1}' \
--preview-window 'right:50%:wrap'
)
if test (count $repos) -eq 0
echo "gtm: nothing selected" >&2
return 1
end
end
# -- Select target org on prad.codes --------------------------------------
set -l owner (
tea organizations list -o simple \
| string trim \
| string match -rv '^$' \
| gum choose --header "Target org on prad.codes"
)
if test -z "$owner"
echo "gtm: no target org selected" >&2
return 1
end
# -- Options --------------------------------------------------------------
set -l extras (
gum choose --no-limit \
--header "Migration options (space to toggle)" \
"wiki" "issues" "labels" "pull-requests" "releases" "milestones" "lfs" "mirror"
)
set -l extra_flags
for opt in $extras
set -a extra_flags "--$opt"
end
# -- Migrate --------------------------------------------------------------
set -l failed
set -l migrated # repo names successfully landed on prad.codes/$owner
for line in $repos
set -l fields (string split \t -- $line)
set -l nwo $fields[1]
set -l is_private $fields[2]
set -l name (string split -m1 / -- $nwo)[2]
set -l flags --name $name --owner $owner \
--clone-url "https://github.com/$nwo.git" \
--service git \
--auth-token $gh_token \
$extra_flags
if test "$is_private" = true
set -a flags --private
end
echo "→ migrating $nwo → prad.codes/$owner/$name"
if tea repos migrate $flags
set -a migrated $name
else
set -a failed $nwo
echo "✗ failed: $nwo" >&2
end
end
# -- Optional local clone -------------------------------------------------
if test (count $migrated) -gt 0
and gum confirm "Clone "(count $migrated)" migrated repo(s) locally via ghg-clone?"
set -l last_dir
for name in $migrated
set -l dir (ghg-clone "https://prad.codes/$owner/$name")
if test -n "$dir"
set last_dir $dir
else
echo "✗ clone failed: $owner/$name" >&2
end
end
# Single repo → drop into it (function runs in-shell, so cd sticks)
if test (count $migrated) -eq 1 -a -n "$last_dir"
cd $last_dir
end
end
# -- Summary --------------------------------------------------------------
if test (count $failed) -gt 0
echo
echo "gtm: "(count $failed)" migration(s) failed:" >&2
printf ' %s\n' $failed >&2
return 1
end
echo
echo "✓ done — "(count $repos)" repo(s) migrated to $owner"
end
@@ -0,0 +1,16 @@
function launch_omp_livediff_overlay --description "Overlay omp on the current kitty window with a livediff split beside it"
# kitty appends -PID to config-file listen_on sockets, so a hardcoded
# --to unix:@kitty-local never matches. kitty @ resolves the real
# address from $KITTY_LISTEN_ON on its own.
if not set -q KITTY_LISTEN_ON
echo "launch_omp_livediff_overlay: no kitty remote-control socket (KITTY_LISTEN_ON unset)" >&2
return 1
end
# omp overlays the current window; capture its window id
set -l wid (kitty @ launch --type=overlay-main --cwd=current omp)
or return
# livediff side by side in the same tab; keep focus on omp
kitty @ launch --match id:$wid --location=vsplit --keep-focus --cwd=current livediff
end
+16
View File
@@ -0,0 +1,16 @@
function lc-elir -d "fzf-pick LeetCode solution files and run /leetcode-eli5 on each via omp"
set -l root (count $argv > /dev/null; and echo $argv[1]; or echo .)
# Match only numbered problem files: 1365.some-slug.js anywhere in the tree
set -l files (fd --type f '^\d+\..+\.(js|ts)$' $root \
| fzf --multi \
--prompt='eli5> ' \
--delimiter=/ \
--with-nth=-3.. \
--preview 'bat --color=always --line-range=:40 {}' \
--preview-window=right:60%)
test -z "$files"; and return 1
for f in $files
echo "── /leetcode-eli5 $f"
omp -p "/leetcode-eli5 $f"
end
end
+8
View File
@@ -0,0 +1,8 @@
function mdcp -a url
if test -z "$url"
echo "usage: mdcp <url>" >&2
return 1
end
curl.md $url | cpy
pst | mdfried
end
+10
View File
@@ -0,0 +1,10 @@
# ~/.config/fish/functions/mdv.fish
function mdp -d "Interactive Markdown selector and previewer"
# Find markdown files up to 3 directories deep
set -l file (fd --type f --extension md --max-depth 3 | fzf --preview 'mdfried {}' --preview-window=right:60%:wrap)
# Execute mdfried on the selected file if one was chosen
if test -n "$file"
mdfried "$file"
end
end
+237
View File
@@ -0,0 +1,237 @@
function nvm --description "Node version manager"
for silent in --silent -s
if set --local index (contains --index -- $silent $argv)
set --erase argv[$index] && break
end
set --erase silent
end
set --local cmd $argv[1]
set --local ver $argv[2]
if set --query silent && ! set --query cmd[1]
echo "nvm: Version number not specified (see nvm -h for usage)" >&2
return 1
end
if ! set --query ver[1] && contains -- "$cmd" install use
for file in .nvmrc .node-version
set file (_nvm_find_up $PWD $file) && read ver <$file && break
end
if ! set --query ver[1]
echo "nvm: Invalid version or missing \".nvmrc\" file" >&2
return 1
end
end
set --local their_version $ver
switch "$cmd"
case -v --version
echo "nvm, version 2.2.18"
case "" -h --help
echo "Usage: nvm install <version> Download and activate the specified Node version"
echo " nvm install Install the version specified in the nearest .nvmrc file"
echo " nvm use <version> Activate the specified Node version in the current shell"
echo " nvm use Activate the version specified in the nearest .nvmrc file"
echo " nvm list List installed Node versions"
echo " nvm list-remote List available Node versions to install"
echo " nvm list-remote <regex> List Node versions matching a given regex pattern"
echo " nvm current Print the currently-active Node version"
echo " nvm uninstall <version> Uninstall the specified Node version"
echo "Options:"
echo " -s, --silent Suppress standard output"
echo " -v, --version Print the version of nvm"
echo " -h, --help Print this help message"
echo "Variables:"
echo " nvm_arch Override architecture, e.g. x64-musl"
echo " nvm_mirror Use a mirror for downloading Node binaries"
echo " nvm_default_version Set the default version for new shells"
echo " nvm_default_packages Install a list of packages every time a Node version is installed"
echo " nvm_data Set a custom directory for storing nvm data"
echo "Examples:"
echo " nvm install latest Install the latest version of Node"
echo " nvm use 14.15.1 Use Node version 14.15.1"
echo " nvm use system Activate the system's Node version"
case install
_nvm_index_update
string match --entire --regex -- (_nvm_version_match $ver) <$nvm_data/.index | read ver alias
if ! set --query ver[1]
echo "nvm: Invalid version number or alias: \"$their_version\"" >&2
return 1
end
if test ! -e $nvm_data/$ver
set --local os (command uname -s | string lower)
set --local ext tar.gz
set --local arch (command uname -m)
set --local tarcmd tar
switch $os
case aix
set arch ppc64
case sunos
case linux
case darwin
case {msys_nt,mingw\*_nt}\*
set os win
set ext zip
set tarcmd bsdtar
case \*
echo "nvm: Unsupported operating system: \"$os\"" >&2
return 1
end
switch $arch
case i\*86
set arch x86
case x86_64
set arch x64
case arm64
string match --regex --quiet "v(?<major>\d+)" $ver
if test "$os" = darwin -a $major -lt 16
set arch x64
end
case armv6 armv6l
set arch armv6l
case armv7 armv7l
set arch armv7l
case armv8 armv8l aarch64
set arch arm64
end
set --query nvm_arch && set arch $nvm_arch
set --local dir "node-$ver-$os-$arch"
set --local url $nvm_mirror/$ver/$dir.$ext
command mkdir -p $nvm_data/$ver
if ! set --query silent
echo -e "Installing Node \x1b[1m$ver\x1b[22m $alias"
echo -e "Fetching \x1b[4m$url\x1b[24m\x1b[7m"
end
if ! command curl -q $silent --progress-bar --location $url |
command $tarcmd --extract --gzip --directory $nvm_data/$ver 2>/dev/null
command rm -rf $nvm_data/$ver
echo -e "\033[F\33[2K\x1b[0mnvm: Invalid mirror or host unavailable: \"$url\"" >&2
return 1
end
set --query silent || echo -en "\033[F\33[2K\x1b[0m"
if test "$os" = win
command mv $nvm_data/$ver/$dir $nvm_data/$ver/bin
else
command mv $nvm_data/$ver/$dir/* $nvm_data/$ver
command rm -rf $nvm_data/$ver/$dir
end
end
if test $ver != "$nvm_current_version"
set --query nvm_current_version && _nvm_version_deactivate $nvm_current_version
_nvm_version_activate $ver
set --query nvm_default_packages[1] && npm install --global $silent $nvm_default_packages
end
set --query silent || printf "Now using Node %s (npm %s) %s\n" (_nvm_node_info)
case use
test $ver = default && set ver $nvm_default_version
_nvm_list | string match --entire --regex -- (_nvm_version_match $ver) | read ver __
if ! set --query ver[1]
echo "nvm: Can't use Node \"$their_version\", version must be installed first" >&2
return 1
end
if test $ver != "$nvm_current_version"
set --query nvm_current_version && _nvm_version_deactivate $nvm_current_version
test $ver != system && _nvm_version_activate $ver
end
set --query silent || printf "Now using Node %s (npm %s) %s\n" (_nvm_node_info)
case uninstall
if test -z "$ver"
echo "nvm: Not enough arguments for command: \"$cmd\"" >&2
return 1
end
test $ver = default && test ! -z "$nvm_default_version" && set ver $nvm_default_version
_nvm_list | string match --entire --regex -- (_nvm_version_match $ver) | read ver __
if ! set -q ver[1]
echo "nvm: Node version not installed or invalid: \"$their_version\"" >&2
return 1
end
set --query silent || printf "Uninstalling Node %s %s\n" $ver (string replace ~ \~ "$nvm_data/$ver/bin/node")
_nvm_version_deactivate $ver
command rm -rf $nvm_data/$ver
case current
_nvm_current
case ls list
_nvm_list | _nvm_list_format (_nvm_current) $argv[2]
case lsr {ls,list}-remote
_nvm_index_update || return
_nvm_list | command awk '
FILENAME == "-" && (is_local[$1] = FNR == NR) { next } {
print $0 (is_local[$1] ? " ✓" : "")
}
' - $nvm_data/.index | _nvm_list_format (_nvm_current) $argv[2]
case \*
echo "nvm: Unknown command or option: \"$cmd\" (see nvm -h for usage)" >&2
return 1
end
end
function _nvm_find_up --argument-names path file
test -e "$path/$file" && echo $path/$file || begin
test ! -z "$path" || return
_nvm_find_up (string replace --regex -- '/[^/]*$' "" $path) $file
end
end
function _nvm_version_match --argument-names ver
string replace --regex -- '^v?(\d+|\d+\.\d+)$' 'v$1.' $ver |
string replace --filter --regex -- '^v?(\d+)' 'v$1' |
string escape --style=regex || string lower '\b'$ver'(?:/\w+)?$'
end
function _nvm_list_format --argument-names current regex
command awk -v current="$current" -v regex="$regex" '
$0 ~ regex {
aliases[versions[i++] = $1] = $2 " " $3
pad = (n = length($1)) > pad ? n : pad
}
END {
if (!i) exit 1
while (i--)
printf((current == versions[i] ? " ▶ " : " ") "%"pad"s %s\n",
versions[i], aliases[versions[i]])
}
'
end
function _nvm_current
command --search --quiet node || return
set --query nvm_current_version && echo $nvm_current_version || echo system
end
function _nvm_node_info
set --local npm_path (string replace bin/npm-cli.js "" (realpath (command --search npm)))
test -f $npm_path/package.json || set --local npm_version_default (command npm --version)
command node --eval "
console.log(process.version)
console.log('$npm_version_default' ? '$npm_version_default': require('$npm_path/package.json').version)
console.log(process.execPath)
" | string replace -- ~ \~
end
+8
View File
@@ -0,0 +1,8 @@
function y
set tmp (mktemp -t "yazi-cwd.XXXXXX")
command yazi $argv --cwd-file="$tmp"
if read -z cwd < "$tmp"; and [ "$cwd" != "$PWD" ]; and test -d "$cwd"
builtin cd -- "$cwd"
end
command rm -f -- "$tmp"
end
+8
View File
@@ -0,0 +1,8 @@
function yy
set tmp (mktemp -t "yazi-cwd.XXXXXX")
command yazi $argv --cwd-file="$tmp"
if read -z cwd < "$tmp"; and [ "$cwd" != "$PWD" ]; and test -d "$cwd"
builtin cd -- "$cwd"
end
rm -f -- "$tmp"
end