mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 19:32:43 +00:00
shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables: ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj (ghg-clone not named ghg: unrelated cargo binary owns that name) - cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files - fish keeps only shell-state functions; converted ones now hit PATH - cp-appid ignore gating moved to .local/bin path - dot_agents/skills: 82 skills materialized from dotstate storage so dot_claude skill symlinks resolve on fresh bootstrap; dotstate profile/symlinks.json released .agents/skills .bashrc .zshrc
This commit is contained in:
1 parent
6776366ce6
commit
6406208b35
637 files changed
+100297
-497
No files matched your search
@@ -0,0 +1,8 @@
|
||||
# c: clear with no args, cd with one.
|
||||
c() {
|
||||
if [ -z "${1-}" ]; then
|
||||
clear
|
||||
else
|
||||
cd "$1"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# cd with zoxide fallback, else builtin cd.
|
||||
cd() {
|
||||
local rc target
|
||||
if [ $# -eq 0 ]; then
|
||||
builtin cd "$HOME"
|
||||
elif [ "$1" = - ]; then
|
||||
builtin cd -
|
||||
elif builtin cd "$1" 2>/dev/null; then
|
||||
: # exact path resolved, nothing else to do
|
||||
elif command -v zoxide >/dev/null 2>&1; then
|
||||
target=$(zoxide query -- "$1" 2>/dev/null)
|
||||
if [ -n "$target" ]; then
|
||||
builtin cd "$target"
|
||||
else
|
||||
builtin cd "$1" # let builtin cd surface the real error
|
||||
fi
|
||||
else
|
||||
builtin cd "$1"
|
||||
fi
|
||||
rc=$?
|
||||
command -v zoxide >/dev/null 2>&1 && zoxide add "$PWD" 2>/dev/null
|
||||
return $rc
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Re-run last command, copy output to clipboard.
|
||||
# History-offset handling: some line editors add the current command to
|
||||
# history before execution, some after — detect instead of assuming.
|
||||
clip-last() {
|
||||
local last copy
|
||||
last=$(fc -ln -1 -1 2>/dev/null)
|
||||
last=${last#"${last%%[![:space:]]*}"} # trim leading whitespace
|
||||
case $last in
|
||||
clip-last*)
|
||||
last=$(fc -ln -2 -2 2>/dev/null)
|
||||
last=${last#"${last%%[![:space:]]*}"}
|
||||
;;
|
||||
esac
|
||||
if [ -z "$last" ]; then
|
||||
echo "clip-last: no previous command in history" >&2
|
||||
return 1
|
||||
fi
|
||||
if command -v wl-copy >/dev/null 2>&1; then
|
||||
copy=wl-copy
|
||||
elif command -v pbcopy >/dev/null 2>&1; then
|
||||
copy=pbcopy
|
||||
else
|
||||
echo "clip-last: no clipboard tool (wl-copy/pbcopy) found" >&2
|
||||
return 1
|
||||
fi
|
||||
eval "$last" | "$copy"
|
||||
command -v notify-send >/dev/null 2>&1 \
|
||||
&& notify-send -a shell "Clipboard" "Command output copied to clipboard"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# ghq get + cd; clone/path logic lives in ~/.local/bin/ghg-clone.
|
||||
# (helper is not named ghg: an unrelated cargo binary owns that name)
|
||||
ghg() {
|
||||
local dir
|
||||
dir=$(ghg-clone "$@") || return
|
||||
cd "$dir" || return
|
||||
clear && onefetch
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Create a repo (prad.codes/github.com) + cd; logic lives in ~/.local/bin/ghn.
|
||||
ghn() {
|
||||
local dir
|
||||
dir=$(command ghn "$@") || return
|
||||
cd "$dir" || return
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Pick a ghq repo / Obsidian vault + cd; picker lives in ~/.local/bin/gho.
|
||||
gho() {
|
||||
local dir
|
||||
dir=$(command gho) || return
|
||||
cd "$dir" || return
|
||||
clear
|
||||
if [ -d .git ]; then
|
||||
onefetch
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# yazi wrapper: cd to the directory yazi was left in on exit.
|
||||
yy() {
|
||||
local tmp cwd
|
||||
tmp=$(mktemp -t "yazi-cwd.XXXXXX")
|
||||
command yazi "$@" --cwd-file="$tmp"
|
||||
cwd=$(cat -- "$tmp" 2>/dev/null)
|
||||
if [ -n "$cwd" ] && [ "$cwd" != "$PWD" ] && [ -d "$cwd" ]; then
|
||||
builtin cd -- "$cwd"
|
||||
fi
|
||||
rm -f -- "$tmp"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -1,3 +0,0 @@
|
||||
function c2p --description "Code2Prompt with fuzzy-search template"
|
||||
fd --type f --extension hbs . $HOME/.config/code2prompt | fzf | xargs code2prompt --template
|
||||
end
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
@@ -1,9 +0,0 @@
|
||||
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
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/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)
|
||||
@@ -1,18 +1,5 @@
|
||||
function ghg -a repo
|
||||
# ghq natively understands https://, ssh://, git@host:…, and host/user/repo
|
||||
ghq get $repo; or return
|
||||
|
||||
# Reduce the input to ghq's on-disk shape: <host>/<user>/<repo>
|
||||
set -l rel (string replace -r '^\w+://' '' -- $repo) # strip scheme (https://, ssh://, git://)
|
||||
set rel (string replace -r '^[^@/]+@' '' -- $rel) # strip user@ (git@github.com:…)
|
||||
set rel (string replace ':' '/' -- $rel) # scp host:path → host/path
|
||||
set rel (string replace -r '\.git$' '' -- $rel) # strip trailing .git
|
||||
|
||||
# bare "user/repo" with no host → default to github.com
|
||||
string match -q '*.*/*' -- $rel; or set rel github.com/$rel
|
||||
|
||||
set -l dir (ghq root)/$rel
|
||||
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
|
||||
zoxide add $dir
|
||||
clear; and onefetch
|
||||
end
|
||||
@@ -1,29 +1,4 @@
|
||||
function ghn -a repo
|
||||
if test -z "$repo"
|
||||
echo "usage: ghn <repo-name>" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
set -l host (gum choose "prad.codes" "github.com")
|
||||
or return 1
|
||||
|
||||
if test "$host" = prad.codes
|
||||
set -l owner (tea organizations list -o simple | string trim | gum choose)
|
||||
or return 1
|
||||
|
||||
tea repos create --name $repo --owner $owner --private
|
||||
or return 1
|
||||
|
||||
ghq get https://prad.codes/$owner/$repo
|
||||
and cd (ghq root)/prad.codes/$owner/$repo
|
||||
else
|
||||
set -l owner (gum choose "prdlk" "wltbs")
|
||||
or return 1
|
||||
|
||||
gh repo create $owner/$repo --private
|
||||
or return 1
|
||||
|
||||
ghq get github.com/$owner/$repo
|
||||
and cd (ghq root)/github.com/$owner/$repo
|
||||
end
|
||||
function ghn -a repo --description 'create repo + cd (logic in ~/.local/bin/ghn)'
|
||||
set -l dir (command ghn $repo); or return
|
||||
cd $dir
|
||||
end
|
||||
@@ -1,21 +1,6 @@
|
||||
function gho
|
||||
set -l vaults ~/Documents/Obsidian/Personal ~/Documents/Obsidian/Walletbase ~/Documents/Obsidian/Research
|
||||
|
||||
set -l selection (ghq list | fzf --prompt="repo: " --tiebreak=end \
|
||||
--bind "tab:transform:if test \"\$FZF_PROMPT\" = 'repo: '
|
||||
echo 'change-prompt(vault: )+reload(printf %s\n $vaults)'
|
||||
else
|
||||
echo 'change-prompt(repo: )+reload(ghq list)'
|
||||
end")
|
||||
or return
|
||||
|
||||
# Vault entries are absolute paths; ghq entries are host/user/repo
|
||||
if string match -q '/*' -- $selection
|
||||
cd $selection
|
||||
else
|
||||
cd (ghq root)/$selection
|
||||
end
|
||||
|
||||
function gho --description 'pick ghq repo / Obsidian vault + cd (picker in ~/.local/bin/gho)'
|
||||
set -l dir (command gho); or return
|
||||
cd $dir; or return
|
||||
clear
|
||||
if test -d .git
|
||||
onefetch
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -1,8 +0,0 @@
|
||||
function mdcp -a url
|
||||
if test -z "$url"
|
||||
echo "usage: mdcp <url>" >&2
|
||||
return 1
|
||||
end
|
||||
curl.md $url | cpy
|
||||
pst | mdfried
|
||||
end
|
||||
@@ -1,10 +0,0 @@
|
||||
# ~/.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
|
||||
@@ -1,86 +0,0 @@
|
||||
function pj --description "fzf package.json script runner with auto-detected package manager"
|
||||
# ── Locate nearest package.json (walk up) ──────────────────────────
|
||||
set -l dir (pwd)
|
||||
set -l pkg ""
|
||||
while true
|
||||
if test -f "$dir/package.json"
|
||||
set pkg "$dir/package.json"
|
||||
break
|
||||
end
|
||||
test "$dir" = / && break
|
||||
set dir (path dirname $dir)
|
||||
end
|
||||
|
||||
if test -z "$pkg"
|
||||
echo "pj: no package.json found in this tree" >&2
|
||||
return 1
|
||||
end
|
||||
set -l root (path dirname $pkg)
|
||||
|
||||
# ── Detect package manager ─────────────────────────────────────────
|
||||
# Priority: packageManager field (corepack) → lockfiles (walking up
|
||||
# past $root to catch monorepo-root locks) → npm fallback
|
||||
set -l pm (jq -r '.packageManager // empty' $pkg | string split '@')[1]
|
||||
|
||||
if test -z "$pm"
|
||||
set -l d $root
|
||||
while test -z "$pm"
|
||||
if test -f "$d/bun.lockb" -o -f "$d/bun.lock"
|
||||
set pm bun
|
||||
else if test -f "$d/pnpm-lock.yaml"
|
||||
set pm pnpm
|
||||
else if test -f "$d/yarn.lock"
|
||||
set pm yarn
|
||||
else if test -f "$d/package-lock.json"
|
||||
set pm npm
|
||||
end
|
||||
test "$d" = / && break
|
||||
set d (path dirname $d)
|
||||
end
|
||||
test -z "$pm" && set pm npm
|
||||
end
|
||||
|
||||
# ── Pick a script ──────────────────────────────────────────────────
|
||||
set -l script (jq -r '.scripts // {} | keys[]' $pkg | fzf \
|
||||
--prompt="$pm run ❯ " \
|
||||
--header="$root" \
|
||||
--height=40% --layout=reverse --border \
|
||||
--preview="jq -r --arg s {} '.scripts[\$s]' $pkg" \
|
||||
--preview-window=up:2:wrap)
|
||||
|
||||
test -z "$script" && return 0
|
||||
|
||||
# ── Run (npm needs `--` to forward extra args) ────────────────────
|
||||
set -l extra $argv
|
||||
if test "$pm" = npm -a (count $extra) -gt 0
|
||||
set extra -- $extra
|
||||
end
|
||||
|
||||
# Wrap long-running server scripts in openlogs (live log tail for agents).
|
||||
# Matches dev/start/serve and namespaced variants like dev:web, start:api.
|
||||
set -l wrapper
|
||||
if string match -qr '^(dev|start|serve|watch)(:.*)?$' -- $script
|
||||
# Resolve the binary: PATH → mise resolution → mise installs glob
|
||||
set -l ol (command -s openlogs)
|
||||
if test -z "$ol"; and command -sq mise
|
||||
set ol (mise which openlogs 2>/dev/null)
|
||||
end
|
||||
if test -z "$ol"
|
||||
set ol ~/.local/share/mise/installs/node/*/bin/openlogs
|
||||
set ol $ol[1]
|
||||
end
|
||||
|
||||
if test -n "$ol" -a -x "$ol"
|
||||
set wrapper $ol
|
||||
else
|
||||
echo "pj: openlogs not found, running unwrapped" >&2
|
||||
end
|
||||
end
|
||||
if test -n "$wrapper"
|
||||
echo "→ $wrapper $pm run $script $argv" >&2
|
||||
$wrapper $pm run $script $extra
|
||||
else
|
||||
echo "→ $pm run $script $argv" >&2
|
||||
$pm run $script $extra
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
# c: clear with no args, cd with one.
|
||||
c() {
|
||||
if [ -z "${1-}" ]; then
|
||||
clear
|
||||
else
|
||||
cd "$1"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# cd with zoxide fallback, else builtin cd.
|
||||
cd() {
|
||||
local rc target
|
||||
if [ $# -eq 0 ]; then
|
||||
builtin cd "$HOME"
|
||||
elif [ "$1" = - ]; then
|
||||
builtin cd -
|
||||
elif builtin cd "$1" 2>/dev/null; then
|
||||
: # exact path resolved, nothing else to do
|
||||
elif command -v zoxide >/dev/null 2>&1; then
|
||||
target=$(zoxide query -- "$1" 2>/dev/null)
|
||||
if [ -n "$target" ]; then
|
||||
builtin cd "$target"
|
||||
else
|
||||
builtin cd "$1" # let builtin cd surface the real error
|
||||
fi
|
||||
else
|
||||
builtin cd "$1"
|
||||
fi
|
||||
rc=$?
|
||||
command -v zoxide >/dev/null 2>&1 && zoxide add "$PWD" 2>/dev/null
|
||||
return $rc
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Re-run last command, copy output to clipboard.
|
||||
# History-offset handling: some line editors add the current command to
|
||||
# history before execution, some after — detect instead of assuming.
|
||||
clip-last() {
|
||||
local last copy
|
||||
last=$(fc -ln -1 -1 2>/dev/null)
|
||||
last=${last#"${last%%[![:space:]]*}"} # trim leading whitespace
|
||||
case $last in
|
||||
clip-last*)
|
||||
last=$(fc -ln -2 -2 2>/dev/null)
|
||||
last=${last#"${last%%[![:space:]]*}"}
|
||||
;;
|
||||
esac
|
||||
if [ -z "$last" ]; then
|
||||
echo "clip-last: no previous command in history" >&2
|
||||
return 1
|
||||
fi
|
||||
if command -v wl-copy >/dev/null 2>&1; then
|
||||
copy=wl-copy
|
||||
elif command -v pbcopy >/dev/null 2>&1; then
|
||||
copy=pbcopy
|
||||
else
|
||||
echo "clip-last: no clipboard tool (wl-copy/pbcopy) found" >&2
|
||||
return 1
|
||||
fi
|
||||
eval "$last" | "$copy"
|
||||
command -v notify-send >/dev/null 2>&1 \
|
||||
&& notify-send -a shell "Clipboard" "Command output copied to clipboard"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# ghq get + cd; clone/path logic lives in ~/.local/bin/ghg-clone.
|
||||
# (helper is not named ghg: an unrelated cargo binary owns that name)
|
||||
ghg() {
|
||||
local dir
|
||||
dir=$(ghg-clone "$@") || return
|
||||
cd "$dir" || return
|
||||
clear && onefetch
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Create a repo (prad.codes/github.com) + cd; logic lives in ~/.local/bin/ghn.
|
||||
ghn() {
|
||||
local dir
|
||||
dir=$(command ghn "$@") || return
|
||||
cd "$dir" || return
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Pick a ghq repo / Obsidian vault + cd; picker lives in ~/.local/bin/gho.
|
||||
gho() {
|
||||
local dir
|
||||
dir=$(command gho) || return
|
||||
cd "$dir" || return
|
||||
clear
|
||||
if [ -d .git ]; then
|
||||
onefetch
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# yazi wrapper: cd to the directory yazi was left in on exit.
|
||||
yy() {
|
||||
local tmp cwd
|
||||
tmp=$(mktemp -t "yazi-cwd.XXXXXX")
|
||||
command yazi "$@" --cwd-file="$tmp"
|
||||
cwd=$(cat -- "$tmp" 2>/dev/null)
|
||||
if [ -n "$cwd" ] && [ "$cwd" != "$PWD" ] && [ -d "$cwd" ]; then
|
||||
builtin cd -- "$cwd"
|
||||
fi
|
||||
rm -f -- "$tmp"
|
||||
}
|
||||
Reference in new issue
Block a user