Skip to content
dots
Esc
↑↓navigate↵open⌘Jpreview
On this page

Scripts

The shell-agnostic executables in ~/.local/bin — usage, behavior, and dependencies for each.

dot_local/bin/ holds standalone sh, bash, and python3 executables. The executable_ prefix is a chezmoi attribute that sets the executable bit and is dropped from the target name, so dot_local/bin/executable_pj becomes ~/.local/bin/pj. bash, zsh, and fish all put ~/.local/bin on PATH, so every shell runs the same implementation.

This page covers the general-purpose scripts. The desktop scripts in the same directory are documented with their compositor:

  • niri-launch, niri-launch-ai, niri-launch-browser, niri-launch-terminal, niri-launch-webapp, cp-appid: niri
  • omarchy-*: Hyprland

Why some helpers are shell functions

A script runs as a child process, and a child cannot change its parent shell’s working directory or read the parent’s in-memory history. Helpers that need to do either are therefore defined as shell functions, ported three times: fish/functions/*.fish, bash/functions/*.bash, and zsh/functions/*.zsh.

Helper Why it must be a function
yy (and fish’s y) cds into the directory yazi exited in
c cds when given an argument
cd Replaces the cd builtin with a zoxide fallback
ghg, ghn, gho cd into a repo that the script picked or cloned
clip-last Reads the shell’s own history ($history[1] in fish, fc in bash/zsh)

For ghg, ghn, and gho, the work lives in a script that prints a directory on stdout (and everything else on stderr). The shell function captures that path and cds into it. ghg’s script is named ghg-clone because an unrelated cargo binary already uses the name ghg.

Several scripts below also exist as fish functions of the same name (ai, c2p, extract_agents, fab, gdc, git-summary, lc-elir, mdcp, mdp, launch_omp_livediff_overlay, gh-sonr-org-migrate). In fish the function runs instead of the script. Both versions behave the same. See fish functions.

Overview

Script Language One line
ai sh Pick an AI CLI with gum and launch it
atuin-pick python3 atuin history picker for a kitty or tmux split
c2p sh code2prompt with an fzf-picked template
extract_agents sh Copy all AGENTS.md files into a directory tree
fab sh fzf picker for Fabric patterns
gdc sh Discard all git changes after confirmation
gh-sonr-org-migrate bash Consolidate *sonr* GitHub orgs into sonr-io
ghg-clone sh ghq get and print the clone path
ghn sh Create a private repo, clone it, print the path
gho sh Pick a ghq repo or Obsidian vault, print the path
git-summary sh Aggregate a repo’s AGENTS.md files with code2prompt
gop sh Open the current repo’s web page
launch_omp_livediff_overlay sh omp overlay plus a livediff split in kitty
lc-elir sh Run /leetcode-eli5 via omp on picked solutions
mdcp sh Fetch a URL as Markdown, copy it, preview it
mdp sh fzf Markdown picker with mdfried preview
pj bash fzf task runner across every project task source
yazi-nvim-open sh yazi opener that targets the host Neovim

ai

ai

Shows gum choose --header 'AI CLI' with claude, opencode, codex, hermes, and fractal, then execs the choice. claude runs as claude --dangerously-skip-permissions; the others run as the bare binary name. Esc or Ctrl-C exits 0.

Needs: gum and the chosen CLI.

atuin-pick

atuin-pick

Opens atuin’s interactive search (atuin search -i --filter-mode directory), limited to history from the current directory. It then execs an interactive fish. If you picked a command, the new fish runs echo '❯ <cmd>'; <cmd> via -C (--init-command), so aliases resolve, a cd persists, and the prompt appears below the output. Esc starts a plain fish.

It is written for launch contexts where atuin’s shell init never ran. It resolves atuin from PATH or ~/.atuin/bin/atuin, and seeds ATUIN_SESSION from atuin uuid if unset.

Launched by: ctrl+shift+p in kitty. kitty.conf opens it in a horizontal split (launch_hs atuin-pick). Under the kitty-tmux profile, tmux.conf runs it with tmux split-window -v -l 25% in the current pane’s directory. See kitty and atuin.

Needs: python3, atuin, fish.

c2p

c2p

Lists *.hbs files under ~/.config/code2prompt with fd, lets you pick one with fzf, and execs code2prompt --template <file>.

Needs: fd, fzf, code2prompt.

extract_agents

extract_agents <output-dir>

Creates <output-dir> and copies every AGENTS.md below the current directory into it with cp --parents, keeping the relative paths.

Needs: fd, GNU cp (--parents).

fab

fab
some-command | fab

Reads ~/.config/fabric/pattern_explainations.json. fzf lists pattern names, the preview pane (right, 60%, wrapped) shows each description, and ctrl-/ toggles the preview. The picked pattern runs as fabric-ai --pattern <name>, falling back to fabric if fabric-ai is not installed. Piped stdin is buffered to a temp file and fed to the pattern; the temp file is removed on exit.

Needs: jq, fzf, fabric-ai or fabric, and the pattern JSON.

gdc

gdc

Asks gum confirm "Discard all uncommitted changes? This is irreversible.". On yes, runs git reset --hard HEAD and git clean -fd.

Needs: gum, git.

gh-sonr-org-migrate

gh-sonr-org-migrate

A bash port of fish/functions/gh-sonr-org-migrate.fish, with the same flow:

Preflight

Checks that gh, gum, and jq exist and that gh auth status succeeds.

Discover

Lists your GitHub orgs whose login contains sonr, excluding the target sonr-io. You pick which to migrate with gum choose --no-limit.

Transfer

For each repo in each org, runs gh api -X POST repos/<org>/<repo>/transfer -f new_owner=sonr-io. If the name already exists in sonr-io, gum input asks for a new name (default <org>-<repo>). An empty name skips the repo.

Settle

Waits 30 seconds (gum spin), because GitHub transfers are asynchronous.

Delete

For every selected org that had no failed transfers and now has zero repos, asks gum confirm and then gh api -X DELETE orgs/<org>.

Needs: gh authenticated as owner of every org involved, gum, jq.

ghg-clone

ghg-clone <repo>

Runs ghq get <repo>, with its output sent to stderr. It then works out the on-disk path: it strips the scheme, any user@, the scp-style colon, and a trailing .git, and prefixes github.com/ when the input has no host. It checks that $(ghq root)/<host>/<user>/<repo> exists, runs zoxide add on it if zoxide is installed, and prints the path.

Accepts https://…, ssh://…, git@host:user/repo, host/user/repo, or user/repo.

Used by: the ghg shell function, which cds into the path, clears the screen, and runs onefetch. The fish function gtm also uses it to clone migrated repos.

Needs: ghq (GHQ_ROOT=~/Code in fish), optional zoxide.

ghn

ghn <repo-name>

Creates a new private repo and clones it:

  • prad.codes (Gitea): pick an org from tea organizations list, then tea repos create --private and ghq get https://prad.codes/<org>/<name>.
  • github.com: pick the owner prdlk or wltbs, then gh repo create <owner>/<name> --private and ghq get.

You pick the host with gum choose. The script prints the clone directory, and the ghn shell function cds into it.

Needs: gum, ghq, and tea or gh.

gho

gho

An fzf picker with two lists. tab switches between them:

  • repo:: ghq list plus the chezmoi source tree, listed as github.com/prdlk/dotfiles and mapped back to ~/.local/share/chezmoi. --tiebreak=end favors matches at the end of the name.
  • vault:: the Obsidian vaults ~/Documents/Obsidian/Personal, Walletbase, and Research.

It prints the chosen directory. fzf’s bind snippets run under SHELL=/bin/sh, so they behave the same whatever the login shell. The gho shell function cds into the result, clears the screen, and runs onefetch in git repos.

Launched by: ctrl-o in fish and zsh.

Needs: ghq, fzf, onefetch (for the wrapper).

git-summary

git-summary [code2prompt flags…]

From anywhere inside a git repo, runs code2prompt <repo-root> --include AGENTS.md --include "**/AGENTS.md" --template ~/.config/code2prompt/agents-md.hbs. Extra flags such as --output-file or --tokens are passed through.

Needs: git, code2prompt, and the agents-md.hbs template.

gop

gop                 # origin, else upstream, else first remote
gop upstream        # a named remote
gop owner/repo      # github.com/owner/repo
gop <url>           # any https/ssh/scp remote URL

Opens a repo’s web page for any forge whose URL layout is https://<host>/<owner>/<repo>, such as github.com and prad.codes. It reduces any remote form to <host>/<owner>/<repo>: it drops credentials, drops the port on ssh:// and git:// remotes, keeps the port and plain http on http:// remotes, and strips .git. It then runs $BROWSER (default xdg-open) on the result.

Needs: git, a browser or xdg-open.

launch_omp_livediff_overlay

launch_omp_livediff_overlay

Inside kitty with remote control enabled, runs kitty @ launch --type=overlay-main --cwd=current omp, then opens livediff in a vsplit next to that window with --keep-focus. It relies on $KITTY_LISTEN_ON rather than a hard-coded socket, because kitty appends -PID to listen_on socket names. It exits with an error if the variable is unset.

Needs: kitty with allow_remote_control/listen_on (kitty), omp (AI agents), livediff.

lc-elir

lc-elir [root]

Finds numbered LeetCode solution files (^\d+\..+\.(js|ts)$, for example 1365.some-slug.js) under root (default .). fzf multi-selects them, showing the last three path segments with a bat preview of the first 40 lines. Each pick runs omp -p "/leetcode-eli5 <file>".

Needs: fd, fzf, bat, omp, and the /leetcode-eli5 omp command.

mdcp

mdcp <url>

Fetches the URL as Markdown with curl.md, copies it with wl-copy or pbcopy, then pastes it back into mdfried for a rendered preview.

Needs: curl.md, mdfried, and wl-clipboard or pbcopy.

mdp

mdp

Lists *.md files up to 3 directories deep with fd. fzf shows an mdfried preview (right, 60%, wrapped), and the pick opens in mdfried.

Needs: fd, fzf, mdfried.

pj

pj [extra args…]

A task runner that searches every task source in the project and lets you pick one with fzf.

Sources. Each is listed only when its runner is installed:

Source Found in Runner
npm scripts package.json package manager from packageManager, else from the lockfile (bun.lock[b], pnpm-lock.yaml, yarn.lock, package-lock.json, searched upward), else npm, then <pm> run
Make GNUmakefile, makefile, Makefile make. Targets come from a text parse of rules and .PHONY; make -p is avoided because it would run $(shell …)
just justfile, .justfile, Justfile, JUSTFILE just
mise mise.toml, .mise.toml, mise/config.toml, .mise/config.toml, .config/mise/config.toml, or a mise-tasks/.mise/tasks/mise/tasks dir mise run. Hidden tasks are skipped, and tasks inherited from parent directories are filtered out
devbox devbox.json shell.scripts devbox run
poe pyproject.toml [tool.poe.tasks] poe, else uv run poe
pdm [tool.pdm.scripts] (skips _* and post_*) pdm run
rye [tool.rye.scripts] rye run
taskipy [tool.taskipy.tasks] task, else uv run task
hatch [tool.hatch.envs.<env>.scripts] as env:name hatch run
console scripts [tool.poetry.scripts], [project.scripts] poetry run for Poetry projects, else uv run

Scanning. Walks from $PWD up to the git toplevel, inclusive. Outside a repo it stops at the first directory that produced tasks.

Picker. Rows are tagged with the tool (npm, pnpm, bun, make, just, mise, poe, py, …), so typing a tool name filters to it. A directory column appears when more than one project contributed tasks. The header shows the nearest project root. The preview (top, 6 lines) shows the task body: the npm script string, the Make recipe, just --show, mise tasks info, the devbox script, or the pyproject entry.

Run. cds into the task’s directory and execs the runner. Extra arguments are forwarded, with -- inserted for npm run, mise, and devbox. Tasks named dev, start, serve, watch, or namespaced variants like dev:web are wrapped in openlogs (found on PATH, via mise which, or in mise’s node installs) for a live log tail. Without openlogs they run unwrapped, with a note.

Env. PJ_QUIET=1 suppresses the “no task sources found” error. The fish down binding uses it.

Launched by: ctrl-p in fish and zsh, and down on an empty fish prompt.

Needs: bash, fzf, jq, python3 3.11+ (for tomllib), plus whichever runners the project uses.

yazi-nvim-open

yazi-nvim-open [vsplit|split|tabedit] <file>…

yazi’s edit opener. When run inside a Neovim terminal ($NVIM set), each file’s absolute path goes to the host Neovim through nvim --server "$NVIM" --remote-expr 'v:lua.EditFromYazi(mode, path)' (defined in the nvim config’s yazi_session.lua). Outside Neovim it execs a normal nvim, and the split modes do nothing. Set YAZI_OPEN_TRACE=<file> to log each call.

Used by: yazi.toml (the edit opener) and keymap.toml (ctrl-v vsplit, ctrl-x split). See yazi.

Needs: nvim, realpath.

Was this page helpful?