Import from root chezmoi source

This commit is contained in:
Prad Nukala committed 2026-10-14 13:32:59 -04:00
commit c8c8c9a000
77 files changed
+2443

No files matched your search

View File
Whitespace-only changes.
+67
View File
@@ -0,0 +1,67 @@
[metadata]
name = "agents"
description = """
AI coding agent CLIs found on PATH. Enter starts the agent's TUI in the current
directory.
"""
requirements = []
# Entries are "<name + description>\t<launch command>\t<binary>". Only agents
# whose binary resolves on PATH are listed; add a row to the table to support
# another one. The launch command may carry flags (claude, as in `ai`).
[source]
command = '''
while IFS='|' read -r bin cmd desc; do
command -v "$bin" >/dev/null 2>&1 &&
printf '%-13s %s\t%s\t%s\n' "$bin" "$desc" "$cmd" "$bin"
done <<'EOF'
omp|omp|oh-my-pi coding agent
claude|claude --dangerously-skip-permissions|Claude Code (Anthropic)
codex|codex|Codex CLI (OpenAI)
opencode|opencode|OpenCode (sst)
crush|crush|Crush (Charm)
pi|pi|pi coding agent
cursor-agent|cursor-agent|Cursor Agent CLI
copilot|copilot|GitHub Copilot CLI
gemini|gemini|Gemini CLI (Google)
agy|agy|Antigravity CLI (Google)
grok|grok|Grok CLI (xAI)
muse|muse|Muse Code (Meta)
ori|ori|Ori (OpenRouter)
hermes|hermes|Hermes Agent (Nous Research)
amp|amp|Amp (Sourcegraph)
droid|droid|Droid (Factory)
qwen|qwen|Qwen Code (Alibaba)
kimi|kimi|Kimi CLI (Moonshot)
goose|goose|goose (Block)
aider|aider|aider
auggie|auggie|Auggie (Augment Code)
kiro-cli|kiro-cli|Kiro CLI (AWS)
cn|cn|Continue CLI
fractal|fractal|fractal
EOF
'''
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
[preview]
command = '''
p=$(command -v '{split:\t:2}')
printf 'command %s\npath %s\n' '{split:\t:1}' "$p"
r=$(readlink -f "$p")
[ "$r" != "$p" ] && printf 'resolves %s\n' "$r"
# ~/.local/bin wrappers install the agent through mise on first run.
tool=$(grep -o 'mise x "[^"]*"' "$r" 2>/dev/null | cut -d'"' -f2)
[ -n "$tool" ] && printf 'mise %s\n' "$tool"
'''
shell = "bash"
[keybindings]
enter = "actions:launch"
[actions.launch]
description = "Start the agent's TUI"
command = "{split:\\t:1}"
shell = "bash"
mode = "execute"
+17
View File
@@ -0,0 +1,17 @@
[metadata]
name = "alias"
description = "A channel to select from shell aliases"
[source]
# you may need to adjust the command depending on your shell
command = "$SHELL -ic 'alias'"
output = "{split:=:0}"
[preview]
command = "$SHELL -ic 'alias' | grep -E '^(alias )?{split:=:0}='"
[ui.preview_panel]
border_type = "none"
scrollbar = false
padding = { left = 2, right = 1, top = 0, bottom = 0 }
size = 30
+9
View File
@@ -0,0 +1,9 @@
[metadata]
name = "bash-history"
description = "A channel to select from your bash history"
requirements = ["bash"]
[source]
command = "sed '1!G;h;$!d' ${HISTFILE:-${HOME}/.bash_history} | awk '!seen[$0]++'"
no_sort = true
frecency = false
+30
View File
@@ -0,0 +1,30 @@
[metadata]
name = "brew-packages"
description = "List and manage Homebrew packages"
requirements = ["brew"]
[source]
command = [
{ name = "Formulae", run = "brew list --formula" },
{ name = "Casks", run = "brew list --cask" },
]
[preview]
command = "brew info '{}'"
[ui]
layout = "portrait"
[keybindings]
ctrl-u = "actions:upgrade"
ctrl-d = "actions:uninstall"
[actions.upgrade]
description = "Upgrade the selected package"
command = "brew upgrade {}"
mode = "execute"
[actions.uninstall]
description = "Uninstall the selected package"
command = "brew uninstall {}"
mode = "execute"
+22
View File
@@ -0,0 +1,22 @@
[metadata]
name = "cargo-commands"
description = "List available cargo commands and extensions"
requirements = ["cargo"]
[source]
command = "cargo --list 2>/dev/null | tail -n +2 | awk '{print $1}'"
[preview]
command = "cargo {} --help 2>/dev/null | head -50"
[ui]
layout = "portrait"
preview_panel = { size = 60, border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[keybindings]
enter = "actions:run"
[actions.run]
description = "Run the selected cargo command"
command = "cargo {}"
mode = "execute"
+17
View File
@@ -0,0 +1,17 @@
[metadata]
name = "cargo-crates"
description = "List installed cargo crates"
requirements = ["cargo"]
[source]
command = "cargo install --list 2>/dev/null | grep -v '^ ' | sed 's/:$//'"
display = "{split: :0} {split: :1}"
output = "{split: :0}"
[preview]
command = "cargo install --list 2>/dev/null | sed -n '/^{split: :0} /,/^[^ ]/p' | head -20"
[actions.uninstall]
description = "Uninstall the selected crate"
command = "cargo uninstall '{split: :0}'"
mode = "execute"
+19
View File
@@ -0,0 +1,19 @@
[metadata]
name = "channels"
description = "Select a television channel"
requirements = ["tv", "bat"]
[source]
command = ["tv list-channels"]
[preview]
command = "bat -pn --color always ${TELEVISION_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/television}/cable/{}.toml"
shell = "bash"
[keybindings]
enter = "actions:channel-enter"
[actions.channel-enter]
description = "Enter a television channel"
command = "tv {} --inline"
mode = "execute"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "crontab"
description = "List and manage crontab entries"
[source]
command = "crontab -l 2>/dev/null | grep -v '^#' | grep -v '^$'"
no_sort = true
frecency = false
[preview]
command = '''echo 'Schedule: {split: :..5}' && echo '' && echo 'Command:' && echo '{split: :5..}' '''
[ui]
layout = "portrait"
preview_panel = { size = 30, border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[actions.edit]
description = "Open crontab for editing"
command = "crontab -e"
mode = "execute"
+26
View File
@@ -0,0 +1,26 @@
[metadata]
name = "dirs"
description = "A channel to select from directories"
requirements = ["fd"]
[source]
command = [
{ name = "Default", run = "fd -t d" },
{ name = "Hidden", run = "fd -t d --hidden" },
]
[preview]
command = "ls -la --color=always '{}'"
[keybindings]
shortcut = "f2"
[actions.cd]
description = "Open a shell in the selected directory"
command = "cd {} && $SHELL"
mode = "execute"
[actions.goto_parent_dir]
description = "Re-opens tv in the parent directory"
command = "tv dirs .. --inline"
mode = "execute"
+32
View File
@@ -0,0 +1,32 @@
[metadata]
name = "docker-compose"
description = "Manage Docker Compose services"
requirements = ["docker"]
[source]
command = "docker compose ps --format '{{.Name}}\t{{.Service}}\t{{.Status}}'"
display = "{split:\t:1} ({split:\t:2})"
output = "{split:\t:1}"
[preview]
command = "docker compose logs --tail=30 --no-log-prefix '{split:\t:1}'"
[actions.up]
description = "Start the selected service"
command = "docker compose up -d '{split:\t:1}'"
mode = "fork"
[actions.down]
description = "Stop and remove the selected service"
command = "docker compose down '{split:\t:1}'"
mode = "fork"
[actions.restart]
description = "Restart the selected service"
command = "docker compose restart '{split:\t:1}'"
mode = "fork"
[actions.logs]
description = "Follow logs of the selected service"
command = "docker compose logs -f '{split:\t:1}'"
mode = "execute"
+56
View File
@@ -0,0 +1,56 @@
[metadata]
name = "docker-containers"
description = "List and manage Docker containers"
requirements = ["docker", "jq"]
[source]
command = [
{ name = "Running", run = "docker ps --format '{{.Names}}\\t{{.Image}}\\t{{.Status}}'" },
{ name = "All", run = "docker ps -a --format '{{.Names}}\\t{{.Image}}\\t{{.Status}}'" },
]
display = "{split:\\t:0} ({split:\\t:2})"
output = "{split:\\t:0}"
[preview]
command = "docker inspect '{split:\\t:0}' | jq -C '.[0] | {Name, State, Config: {Image: .Config.Image, Cmd: .Config.Cmd}, NetworkSettings: {IPAddress: .NetworkSettings.IPAddress}}'"
[ui]
layout = "portrait"
[keybindings]
ctrl-s = "actions:start"
f2 = "actions:stop"
ctrl-r = "actions:restart"
ctrl-l = "actions:logs"
ctrl-e = "actions:exec"
ctrl-d = "actions:remove"
[actions.start]
description = "Start the selected container"
command = "docker start '{split:\\t:0}'"
mode = "fork"
[actions.stop]
description = "Stop the selected container"
command = "docker stop '{split:\\t:0}'"
mode = "fork"
[actions.restart]
description = "Restart the selected container"
command = "docker restart '{split:\\t:0}'"
mode = "fork"
[actions.logs]
description = "Follow logs of the selected container"
command = "docker logs -f '{split:\\t:0}'"
mode = "execute"
[actions.exec]
description = "Execute shell in the selected container"
command = "docker exec -it '{split:\\t:0}' /bin/sh"
mode = "execute"
[actions.remove]
description = "Remove the selected container"
command = "docker rm '{split:\\t:0}'"
mode = "execute"
+34
View File
@@ -0,0 +1,34 @@
[metadata]
name = "docker-images"
description = "A channel to select from Docker images"
requirements = ["docker", "jq"]
[source]
command = "docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}'"
output = "{split: :-1}"
[preview]
command = "docker image inspect '{split: :-1}' | jq -C"
[ui]
layout = "portrait"
[keybindings]
ctrl-r = "actions:run"
ctrl-d = "actions:remove"
ctrl-s = "actions:shell"
[actions.run]
description = "Run a container from the selected image"
command = "docker run -it '{split: :-1}'"
mode = "execute"
[actions.shell]
description = "Run a shell in the selected image"
command = "docker run -it '{split: :-1}' /bin/sh"
mode = "execute"
[actions.remove]
description = "Remove the selected image"
command = "docker rmi '{split: :-1}'"
mode = "execute"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "docker-networks"
description = "List and manage Docker networks"
requirements = ["docker", "jq"]
[source]
command = "docker network ls --format '{{.Name}}\t{{.Driver}}\t{{.Scope}}'"
display = "{split:\t:0} ({split:\t:1}, {split:\t:2})"
output = "{split:\t:0}"
[preview]
command = "docker network inspect '{split:\t:0}' | jq -C '.[0] | {Name, Driver, Scope, IPAM, Containers: (.Containers // {} | to_entries | map({name: .value.Name, ipv4: .value.IPv4Address}))}'"
[ui]
layout = "portrait"
[actions.remove]
description = "Remove the selected network"
command = "docker network rm '{split:\t:0}'"
mode = "execute"
+25
View File
@@ -0,0 +1,25 @@
[metadata]
name = "docker-volumes"
description = "List and manage Docker volumes"
requirements = ["docker", "jq"]
[source]
command = "docker volume ls --format '{{.Name}}\t{{.Driver}}'"
display = "{split:\t:0} ({split:\t:1})"
output = "{split:\t:0}"
[preview]
command = "docker volume inspect '{split:\t:0}' | jq -C '.[0]'"
[ui]
layout = "portrait"
[actions.remove]
description = "Remove the selected volume"
command = "docker volume rm '{split:\t:0}'"
mode = "execute"
[actions.inspect]
description = "Inspect the selected volume in a pager"
command = "docker volume inspect '{split:\t:0}' | jq -C '.[0]' | less -R"
mode = "execute"
+19
View File
@@ -0,0 +1,19 @@
[metadata]
name = "dotfiles"
description = "A channel to select from your user's dotfiles"
requirements = ["fd", "bat"]
[source]
command = "fd -t f . $HOME/.config"
[preview]
command = "bat -n --color=always '{}'"
[keybindings]
enter = "actions:edit"
[actions.edit]
description = "Edit the selected dotfile"
command = "${EDITOR:-vim} {}"
shell = "bash"
mode = "execute"
+31
View File
@@ -0,0 +1,31 @@
[metadata]
name = "downloads"
description = "Browse recent files in Downloads folder"
requirements = ["fd", "bat"]
[source]
command = "fd -t f . ~/Downloads 2>/dev/null | head -200"
[preview]
command = "bat -n --color=always '{}' 2>/dev/null || file '{}'"
env = { BAT_THEME = "ansi" }
[keybindings]
enter = "actions:open"
ctrl-d = "actions:delete"
ctrl-m = "actions:move"
[actions.open]
description = "Open the selected file with default application"
command = "xdg-open {} 2>/dev/null || open {}"
mode = "fork"
[actions.delete]
description = "Delete the selected file"
command = "rm -i {}"
mode = "execute"
[actions.move]
description = "Move the selected file to current directory"
command = "mv {} ."
mode = "fork"
+22
View File
@@ -0,0 +1,22 @@
[metadata]
name = "env"
description = "A channel to select from environment variables"
[source]
command = "printenv"
output = "{split:=:1..}" # output the value
[preview]
command = "echo '{split:=:1..}'"
[ui]
layout = "portrait"
preview_panel = { size = 20, header = "{split:=:0}", border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[keybindings]
shortcut = "f3"
[actions.name]
description = "Output the variable name instead of the value"
command = "echo '{split:=:0}'"
mode = "execute"
+33
View File
@@ -0,0 +1,33 @@
[metadata]
name = "files"
description = "A channel to select files and directories"
requirements = ["fd", "bat", "mtime-sort"]
# Most recently modified first; tv ranks matches and frecency on top.
[source]
command = [
{ name = "Default", run = "fd -t f | mtime-sort" },
{ name = "Hidden", run = "fd -t f -H | mtime-sort" },
]
shell = "bash"
[preview]
command = "bat -n --color=always '{}'"
env = { BAT_THEME = "ansi" }
# ctrl-e (edit) and ctrl-h (goto_parent_dir) are bound in config.toml.
[keybindings]
shortcut = "f1"
[actions.edit]
description = "Opens the selected entries in nvim"
# Redirect to the tty: the fish ctrl-t binding runs tv inside a command
# substitution, so stdout is a pipe there.
command = "nvim {} </dev/tty >/dev/tty"
shell = "bash"
# use `mode = "fork"` if you want to return to tv afterwards
mode = "execute"
[actions.goto_parent_dir]
description = "Re-opens tv in the parent directory"
command = "tv files .. --inline"
mode = "execute"
+9
View File
@@ -0,0 +1,9 @@
[metadata]
name = "fish-history"
description = "A channel to select from your fish history"
requirements = ["fish"]
[source]
command = "fish -c 'history'"
no_sort = true
frecency = false
+16
View File
@@ -0,0 +1,16 @@
[metadata]
name = "fonts"
description = "List installed system fonts"
requirements = ["fc-list"]
[source]
command = "fc-list --format='%{family}\n' | sort -uf"
[preview]
command = "fc-list '{}' --format='%{file}\n%{style}\n%{family}\n' | head -20"
[ui.preview_panel]
border_type = "none"
scrollbar = false
padding = { left = 2, right = 1, top = 0, bottom = 0 }
size = 70
+47
View File
@@ -0,0 +1,47 @@
[metadata]
name = "gh-issues"
description = "List GitHub issues for the current repo"
requirements = ["gh", "jq"]
[source]
command = '''gh issue list --state open --limit 100 --json number,title,createdAt,author,labels | jq -r 'sort_by(.createdAt) | reverse | .[] | " \u001b[32m#\(.number)\u001b[39m \(.title) \u001b[33m@\(.author.login)\u001b[39m" + (if (.labels | length) > 0 then " " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"] | join(" ")) else "" end)'
'''
ansi = true
output = "{strip_ansi|split:#:1|split: :0}"
[ui]
layout = "portrait"
preview_panel = { header = '{strip_ansi|split:#:1|split: :0}', border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[preview]
command = '''gh issue view '{strip_ansi|split:#:1|split: :0}' --json number,title,body,state,author,createdAt,updatedAt,labels,assignees | jq -r '
" " + .title,
" #" + (.number | tostring),
"",
" \u001b[36mStatus:\u001b[39m \u001b[32m" + .state + "\u001b[39m",
" \u001b[36mAuthor:\u001b[39m \u001b[33m" + .author.login + "\u001b[39m",
" \u001b[36mCreated:\u001b[39m " + (.createdAt | fromdateiso8601 | (now - .) | if . < 3600 then (./60|floor|tostring) + " minutes ago" elif . < 86400 then (./3600|floor|tostring) + " hours ago" else (./86400|floor|tostring) + " days ago" end),
" \u001b[36mUpdated:\u001b[39m " + (.updatedAt | fromdateiso8601 | (now - .) | if . < 3600 then (./60|floor|tostring) + " minutes ago" elif . < 86400 then (./3600|floor|tostring) + " hours ago" else (./86400|floor|tostring) + " days ago" end),
(if (.labels | length) > 0 then " \u001b[36mLabels:\u001b[39m " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"] | join(" ")) else "" end),
(if (.assignees | length) > 0 then " \u001b[36mAssignees:\u001b[39m " + ([.assignees[].login] | join(", ")) else "" end),
"",
" \u001b[90m────────────────────────────────────────────────────────────\u001b[39m",
"",
(.body // "")'
'''
[actions.open]
description = "Open the issue in the browser"
command = "gh issue view {strip_ansi|split:#:1|split: :0} --web"
mode = "fork"
[actions.close]
description = "Close the selected issue"
command = "gh issue close {strip_ansi|split:#:1|split: :0}"
mode = "fork"
[actions.comment]
description = "Add a comment to the selected issue"
command = "gh issue comment {strip_ansi|split:#:1|split: :0}"
mode = "execute"
+52
View File
@@ -0,0 +1,52 @@
[metadata]
name = "gh-prs"
description = "List GitHub PRs for the current repo"
requirements = ["gh", "jq"]
[source]
command = '''gh pr list --state open --limit 100 --json number,title,createdAt,author,labels | jq -r 'sort_by(.createdAt) | reverse | .[] | " \u001b[32m#\(.number)\u001b[39m \(.title) \u001b[33m@\(.author.login)\u001b[39m" + (if (.labels | length) > 0 then " " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"] | join(" ")) else "" end)'
'''
output = '{strip_ansi|split:#:1|split: :0}'
ansi = true
[ui]
layout = "portrait"
[preview]
command = '''gh pr view '{strip_ansi|split:#:1|split: :0}' --json number,title,body,state,headRefName,baseRefName,author,createdAt,updatedAt,mergeable,changedFiles,additions,deletions,labels,assignees,reviewDecision,headRepository,headRepositoryOwner | jq -r '
" " + .title,
" #" + (.number | tostring),
"",
" \u001b[36mStatus:\u001b[39m \u001b[32m" + .state + "\u001b[39m " + .baseRefName + " ← " + .headRefName,
" \u001b[36mRepo:\u001b[39m \u001b[34m" + (.headRepositoryOwner.login) + "/" + (.headRepository.name) + "\u001b[39m",
" \u001b[36mAuthor:\u001b[39m \u001b[33m" + .author.login + "\u001b[39m",
" \u001b[36mCreated:\u001b[39m " + (.createdAt | fromdateiso8601 | (now - .) | if . < 3600 then (./60|floor|tostring) + " minutes ago" elif . < 86400 then (./3600|floor|tostring) + " hours ago" else (./86400|floor|tostring) + " days ago" end),
" \u001b[36mUpdated:\u001b[39m " + (.updatedAt | fromdateiso8601 | (now - .) | if . < 3600 then (./60|floor|tostring) + " minutes ago" elif . < 86400 then (./3600|floor|tostring) + " hours ago" else (./86400|floor|tostring) + " days ago" end),
(if (.labels | length) > 0 then " \u001b[36mLabels:\u001b[39m " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"] | join(" ")) else "" end),
" \u001b[36mMerge Status:\u001b[39m " + (if .mergeable == "MERGEABLE" then "\u001b[32m✓ Clean\u001b[39m" elif .mergeable == "CONFLICTING" then "\u001b[31m✗ Dirty\u001b[39m" else "\u001b[33m? Unknown\u001b[39m" end),
" \u001b[36mChanges:\u001b[39m " + (.changedFiles | tostring) + " files \u001b[32m+" + (.additions | tostring) + "\u001b[39m \u001b[31m-" + (.deletions | tostring) + "\u001b[39m",
"",
" \u001b[90m────────────────────────────────────────────────────────────\u001b[39m",
"",
(.body // "")'
'''
[actions.open]
description = "Open the PR in the browser"
command = "gh pr view {strip_ansi|split:#:1|split: :0} --web"
mode = "execute"
[actions.checkout]
description = "Checkout the PR branch locally"
command = "gh pr checkout {strip_ansi|split:#:1|split: :0}"
mode = "execute"
[actions.merge]
description = "Merge the selected PR"
command = "gh pr merge {strip_ansi|split:#:1|split: :0}"
mode = "execute"
[actions.diff]
description = "View the PR diff"
command = "gh pr diff {strip_ansi|split:#:1|split: :0} | less"
mode = "execute"
+87
View File
@@ -0,0 +1,87 @@
[metadata]
name = "ghq-repos"
description = """
ghq repositories (plus the chezmoi source tree) and Obsidian vaults, newest
git activity first. Enter prints the directory; the gho shell function (ctrl-o
in fish, zsh and bash) cds into it.
"""
requirements = ["ghq", "fd", "git", "serie", "onefetch", "mtime-sort"]
# Entries are "<name>\t<absolute path>": the name is what you fuzzy-match, the
# path is what enter prints. ctrl-s cycles repos → vaults.
[source]
command = [
{ name = "Repos", run = '''
root=$(ghq root)
{
# Checkouts living outside ghq root, listed first so they win over a ghq
# clone of the same repo: chezmoi's source (github.com/prdlk/dotfiles) and
# the nvim config (github.com/prdlk/nvim).
c=$HOME/.local/share/chezmoi
[ -d "$c/.git" ] && printf 'github.com/prdlk/dotfiles\t%s\n' "$c"
n=$HOME/.config/nvim
[ -d "$n/.git" ] && printf 'github.com/prdlk/nvim\t%s\n' "$n"
ghq list | awk -v r="$root" '{ print $0 "\t" r "/" $0 }'
} | awk -F '\t' '!seen[$1]++' | mtime-sort --repo
''' },
{ name = "Vaults", run = '''
v=$HOME/Documents/Obsidian
fd -H -t d -d 3 '^\.obsidian$' "$v" -x dirname |
awk -v r="$v/" '{ print substr($0, length(r) + 1) "\t" $0 }' |
mtime-sort
''' },
]
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
# Two previews, ctrl-f cycles: commit graph (default), then onefetch. serie
# itself cannot draw here -- it is a fullscreen TUI that paints the graph with
# kitty images and has no print mode -- so ctrl-g (history) opens it.
[preview]
command = [
'''
d='{split:\t:1}'
if [ -e "$d/.git" ]; then
git -C "$d" log --graph --all --color=always --date=relative -n 300 \
--format='%C(yellow)%h%C(reset) %C(blue)%<(12,trunc)%ad%C(reset) %s%C(auto)%d' 2>&1
else
eza --tree --level 2 --color=always "$d" 2>/dev/null || ls -la "$d"
fi
''',
'''
d='{split:\t:1}'
if [ -e "$d/.git" ]; then
onefetch --no-art "$d" 2>&1
else
eza --tree --level 2 --color=always "$d" 2>/dev/null || ls -la "$d"
fi
''',
]
shell = "bash"
# ctrl-e (edit) is bound globally in config.toml.
[keybindings]
ctrl-b = "actions:browse"
ctrl-g = "actions:history"
[actions.edit]
description = "Open the repository in nvim"
# /dev/tty: the gho wrapper captures tv's stdout to read the picked path.
command = "cd '{split:\\t:1}' && nvim . </dev/tty >/dev/tty"
shell = "bash"
mode = "execute"
[actions.browse]
description = "Open the repository's web page (gop)"
command = "cd '{split:\\t:1}' && gop >/dev/null 2>&1"
shell = "bash"
mode = "fork"
[actions.history]
description = "Browse the commit graph in serie"
# Fork: quitting serie (q) returns to the picker. /dev/tty for the same reason
# as edit; serie also needs a real tty to detect kitty graphics.
command = "cd '{split:\\t:1}' && [ -e .git ] && serie </dev/tty >/dev/tty"
shell = "bash"
mode = "fork"
+37
View File
@@ -0,0 +1,37 @@
[metadata]
name = "git-branch"
description = "A channel to select from git branches"
requirements = ["git"]
[source]
command = "git --no-pager branch --all --format=\"%(refname:short)\""
output = "{split: :0}"
[preview]
command = "git show -p --stat --pretty=fuller --color=always '{0}'"
[keybindings]
enter = "actions:checkout"
ctrl-d = "actions:delete"
ctrl-m = "actions:merge"
ctrl-r = "actions:rebase"
[actions.checkout]
description = "Checkout the selected branch"
command = "git checkout '{0}'"
mode = "execute"
[actions.delete]
description = "Delete the selected branch"
command = "git branch -d '{0}'"
mode = "execute"
[actions.merge]
description = "Merge the selected branch into current branch"
command = "git merge '{0}'"
mode = "execute"
[actions.rebase]
description = "Rebase current branch onto the selected branch"
command = "git rebase '{0}'"
mode = "execute"
+24
View File
@@ -0,0 +1,24 @@
[metadata]
name = "git-deletions"
description = "A channel to list files which were deleted from the Git repository"
requirements = ["git", "rg", "bat"]
[source]
command = "git log --diff-filter=D --summary | rg 'delete mode \\d+' -r '' --trim"
[preview]
command = ["git show \"$(git rev-list -n1 HEAD -- '{}')^:{}\" | bat -n --color always --file-name '{}'", "git show --summary \"$(git rev-list -n1 HEAD -- '{}')\""]
[keybindings]
enter = "actions:print_commit"
ctrl-r = "actions:restore_file"
[actions.print_commit]
description = "Print the latest commit where file was deleted"
command = "echo \"$(git rev-list -n1 HEAD -- {})\""
mode = "execute"
[actions.restore_file]
description = "Restore the selected file"
command = "git checkout \"$(git rev-list -n1 HEAD -- {})^\" -- {}"
+34
View File
@@ -0,0 +1,34 @@
[metadata]
name = "git-diff"
description = "A channel to select files from git diff commands"
requirements = ["git"]
[source]
command = "git diff --name-only HEAD"
[preview]
command = "git diff HEAD --color=always -- '{}'"
[ui]
layout = "portrait"
[keybindings]
ctrl-s = "actions:stage"
ctrl-r = "actions:restore"
ctrl-e = "actions:edit"
[actions.stage]
description = "Stage the selected file"
command = "git add {}"
mode = "fork"
[actions.restore]
description = "Discard changes in the selected file"
command = "git restore {}"
mode = "fork"
[actions.edit]
description = "Open the selected file in editor"
command = "${EDITOR:-vim} {}"
shell = "bash"
mode = "execute"
+35
View File
@@ -0,0 +1,35 @@
[metadata]
name = "git-files"
description = """
Files tracked in the current git repository, most recently modified first
(all files under the cwd outside a repo). Enter opens the selection in nvim;
bound to ctrl-e in fish, zsh and bash.
"""
requirements = ["git", "fd", "bat", "mtime-sort"]
[source]
command = [
{ name = "Tracked", run = '''
if top=$(git rev-parse --show-toplevel 2>/dev/null); then
git -c core.quotePath=false ls-files -- "$top"
else
fd -t f
fi | mtime-sort
''' },
{ name = "All", run = "fd -t f -H -E .git | mtime-sort" },
]
shell = "bash"
[preview]
command = "bat -n --color=always '{}'"
env = { BAT_THEME = "ansi" }
# ctrl-e (edit) is bound globally in config.toml.
[keybindings]
enter = "actions:edit"
[actions.edit]
description = "Opens the selected entries in nvim"
command = "nvim {} </dev/tty >/dev/tty"
shell = "bash"
mode = "execute"
+34
View File
@@ -0,0 +1,34 @@
[metadata]
name = "git-log"
description = "A channel to select from git log entries"
requirements = ["git"]
[source]
command = "git log --graph --pretty=format:'%C(yellow)%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --color=always"
output = "{strip_ansi|split: :1}"
ansi = true
no_sort = true
frecency = false
[preview]
command = "git show -p --stat --pretty=fuller --color=always '{strip_ansi|split: :1}' | head -n 1000"
[keybindings]
ctrl-y = "actions:cherry-pick"
ctrl-r = "actions:revert"
ctrl-o = "actions:checkout"
[actions.cherry-pick]
description = "Cherry-pick the selected commit"
command = "git cherry-pick '{strip_ansi|split: :1}'"
mode = "execute"
[actions.revert]
description = "Revert the selected commit"
command = "git revert '{strip_ansi|split: :1}'"
mode = "execute"
[actions.checkout]
description = "Checkout the selected commit"
command = "git checkout '{strip_ansi|split: :1}'"
mode = "execute"
+28
View File
@@ -0,0 +1,28 @@
[metadata]
name = "git-reflog"
description = "A channel to select from git reflog entries"
requirements = ["git"]
[source]
command = "git reflog --decorate --color=always"
output = "{0|strip_ansi}"
ansi = true
no_sort = true
frecency = false
[preview]
command = "git show -p --stat --pretty=fuller --color=always '{0|strip_ansi}'"
[keybindings]
ctrl-o = "actions:checkout"
ctrl-r = "actions:reset"
[actions.checkout]
description = "Checkout the selected reflog entry"
command = "git checkout '{0|strip_ansi}'"
mode = "execute"
[actions.reset]
description = "Reset --hard to the selected reflog entry"
command = "git reset --hard '{0|strip_ansi}'"
mode = "execute"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "git-remotes"
description = "List and manage git remotes"
requirements = ["git"]
[source]
command = "git remote"
[preview]
command = "git remote show '{}'"
[actions.fetch]
description = "Fetch from the selected remote"
command = "git fetch {}"
mode = "execute"
[actions.remove]
description = "Remove the selected remote"
command = "git remote remove {}"
mode = "execute"
+31
View File
@@ -0,0 +1,31 @@
[metadata]
name = "git-repos"
requirements = ["fd", "git"]
description = """
A channel to select from git repositories on your local machine.
This channel uses `fd` to find directories that contain a `.git` subdirectory, and then allows you to preview the git log of the selected repository.
"""
[source]
# this is a macos version of the command. While perfectly usable on linux, you may want to tweak it a bit.
command = "fd -g .git -HL -t d -d 10 --prune ~ -E 'Library' -E 'Application Support' --exec dirname '{}'"
display = "{split:/:-1}"
[preview]
command = "cd '{}'; git log -n 200 --pretty=medium --all --graph --color"
[keybindings]
enter = "actions:cd"
ctrl-e = "actions:edit"
[actions.cd]
description = "Open a new shell in the selected repository"
command = "cd {} && $SHELL"
mode = "execute"
[actions.edit]
description = "Open the repository in editor"
command = "${EDITOR:-vim} {}"
shell = "bash"
mode = "execute"
+37
View File
@@ -0,0 +1,37 @@
[metadata]
name = "git-stash"
description = "Browse and manage git stash entries"
requirements = ["git"]
[source]
command = "git stash list --color=always"
ansi = true
output = "{strip_ansi|split:\\::0}"
no_sort = true
frecency = false
[preview]
command = "git stash show -p --color=always '{strip_ansi|split:\\::0}'"
[ui]
layout = "portrait"
[keybindings]
enter = "actions:apply"
ctrl-p = "actions:pop"
ctrl-d = "actions:drop"
[actions.apply]
description = "Apply the selected stash"
command = "git stash apply '{strip_ansi|split:\\::0}'"
mode = "execute"
[actions.pop]
description = "Pop the selected stash (apply and remove)"
command = "git stash pop '{strip_ansi|split:\\::0}'"
mode = "execute"
[actions.drop]
description = "Drop the selected stash"
command = "git stash drop '{strip_ansi|split:\\::0}'"
mode = "execute"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "git-submodules"
description = "List and manage git submodules"
requirements = ["git"]
[source]
command = "git submodule status | awk '{print $2}'"
[preview]
command = "git -C '{}' log --oneline -10 --color=always"
[actions.update]
description = "Update the selected submodule"
command = "git submodule update --init --recursive {}"
mode = "execute"
[actions.sync]
description = "Sync the selected submodule URL"
command = "git submodule sync {}"
mode = "execute"
+26
View File
@@ -0,0 +1,26 @@
[metadata]
name = "git-tags"
description = "Browse and checkout git tags"
requirements = ["git"]
[source]
command = "git tag --sort=-creatordate"
no_sort = true
frecency = false
[preview]
command = "git show --color=always '{}'"
[keybindings]
enter = "actions:checkout"
ctrl-d = "actions:delete"
[actions.checkout]
description = "Checkout the selected tag"
command = "git checkout {}"
mode = "execute"
[actions.delete]
description = "Delete the selected tag"
command = "git tag -d {}"
mode = "execute"
+24
View File
@@ -0,0 +1,24 @@
[metadata]
name = "git-worktrees"
description = "List and switch between git worktrees"
requirements = ["git"]
[source]
command = "git worktree list --porcelain | grep '^worktree' | cut -d' ' -f2-"
[preview]
command = "cd '{}' && git log --oneline -10 --color=always && echo && git status --short"
[keybindings]
enter = "actions:cd"
ctrl-d = "actions:remove"
[actions.cd]
description = "Change to the selected worktree"
command = "cd {} && $SHELL"
mode = "execute"
[actions.remove]
description = "Remove the selected worktree"
command = "git worktree remove {}"
mode = "execute"
+21
View File
@@ -0,0 +1,21 @@
[metadata]
name = "images"
description = "Browse image files with preview"
requirements = ["fd", "chafa"]
[source]
command = [
{ name = "Default", run = "fd -t f -e png -e jpg -e jpeg -e gif -e webp -e bmp -e svg ." },
{ name = "Hidden", run = "fd -t f -e png -e jpg -e jpeg -e gif -e webp -e bmp -e svg -H ." },
]
[preview]
command = "chafa -s 80x40 '{}' 2>/dev/null || file '{}'"
[keybindings]
enter = "actions:open"
[actions.open]
description = "Open the selected image with default viewer"
command = "xdg-open {} 2>/dev/null || open {}"
mode = "fork"
+57
View File
@@ -0,0 +1,57 @@
[metadata]
name = "jobs"
description = """
The current fish shell's background jobs (stopped or running). tv runs in a
child process and cannot see the shell's job table, so the ctrl-z binding
(_jobs_bind) writes it to the file named by $TV_JOBS; enter prints the job id
and the binding runs `fg %<id>`; ctrl-d kills the job. Opened any other way,
the list is empty.
"""
requirements = ["ps"]
# Record: "%<id> <state> <command>", job id, process group (tab separated).
# Newest job first, the order fish's `jobs` prints.
[source]
command = '[ -n "$TV_JOBS" ] && [ -r "$TV_JOBS" ] && cat "$TV_JOBS"'
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
[preview]
command = '''
pg='{split:\t:2}'
# No other braces in this script: tv would read them as templates. procps
# `ps -g` matches sessions, not process groups, so filter on pgid by hand.
pids=$(ps -e -o pgid=,pid= --sort=start_time | while read -r g p; do [ "$g" = "$pg" ] && echo "$p"; done)
if [ -z "$pids" ]; then
echo "process group $pg is gone"
exit 0
fi
first=$(echo "$pids" | head -n1)
printf '\033[1mcwd\033[0m %s\n' "$(readlink "/proc/$first/cwd" 2>/dev/null || echo '?')"
printf '\033[1mstarted\033[0m %s\n\n' "$(ps -o lstart= -p "$first" | sed 's/^ *//')"
# STAT T = stopped (ctrl-z), S/R = running in the background.
ps -o pid,stat,etime,pcpu,rss,args --forest -p "$(echo $pids | tr ' ' ,)"
'''
shell = "bash"
[keybindings]
ctrl-d = ["actions:kill", "reload_source"]
# SIGTERM to the whole process group, then SIGCONT: a stopped job holds TERM
# until it runs again. The row leaves $TV_JOBS only once the group is gone
# (up to 1 s), so reload_source keeps a job that ignored TERM. No braces: tv
# would read them as templates.
[actions.kill]
description = "Kill the job (SIGTERM to its process group)"
command = '''
pg='{split:\t:2}'
kill -TERM -- "-$pg" 2>/dev/null
kill -CONT -- "-$pg" 2>/dev/null
for i in 1 2 3 4 5 6 7 8 9 10; do kill -0 -- "-$pg" 2>/dev/null || break; sleep 0.1; done
if ! kill -0 -- "-$pg" 2>/dev/null && [ -w "$TV_JOBS" ]; then
awk -F '\t' -v g="$pg" '$3 != g' "$TV_JOBS" >"$TV_JOBS.new" && mv "$TV_JOBS.new" "$TV_JOBS"
fi
'''
shell = "bash"
mode = "fork"
+24
View File
@@ -0,0 +1,24 @@
[metadata]
name = "journal"
description = "Browse systemd journal log identifiers and their logs"
requirements = ["journalctl"]
[source]
command = "journalctl --field SYSLOG_IDENTIFIER 2>/dev/null | sort -f"
[preview]
command = "journalctl -b --no-pager -o short-iso -n 50 SYSLOG_IDENTIFIER='{}' 2>/dev/null"
[ui]
layout = "portrait"
preview_panel = { size = 70, border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[actions.logs]
description = "Follow live logs for the selected identifier"
command = "journalctl -f SYSLOG_IDENTIFIER={}"
mode = "execute"
[actions.full]
description = "View all logs for the selected identifier in a pager"
command = "journalctl -b --no-pager -o short-iso SYSLOG_IDENTIFIER={} | less"
mode = "fork"
+18
View File
@@ -0,0 +1,18 @@
[metadata]
name = "just-recipes"
description = "A channel to select recipes from Justfiles"
requirements = ["just"]
[source]
command = ["just --summary | tr '[:blank:]' '\n'"]
[preview]
command = "just -s {}"
[keybindings]
f5 = "actions:execute-recipe"
[actions.execute-recipe]
description = "Execute a justfile recipe"
command = "just {}"
mode = "execute"
+75
View File
@@ -0,0 +1,75 @@
[metadata]
name = "kitty-days"
description = """
Daily kitty sessions saved by ~/.config/kitty/day_session.py, last modified
first. The preview lists each tab's windows (cwd, the program restored in it)
with the tail of its saved scrollback. Enter opens the day in a new kitty.
"""
requirements = ["kitty", "python3", "mtime-sort"]
# Entries are "<day> <weekday> week of <monday> <n> tabs\t<session file>".
[source]
command = '''
for f in "$HOME"/.local/share/kitty-sessions/*/*.kitty-session; do
[ -e "$f" ] || continue
day=$(basename "$f" .kitty-session)
printf '%s %s week of %s %s tabs\t%s\n' "$day" "$(date -d "$day" +%a)" \
"$(basename "$(dirname "$f")")" "$(grep -c '^new_tab' "$f")" "$f"
done | mtime-sort
'''
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
# The script avoids braces: tv would read them as templates.
[preview]
command = '''
python3 - '{split:\t:1}' <<'PY'
import json, os, shlex, sys
FLAG = 'kitty-unserialize-data='
home = os.path.expanduser('~')
tab, cwd = 0, ''
for line in open(sys.argv[1]).read().splitlines():
word, _, rest = line.partition(' ')
if word == 'new_tab':
tab += 1
print('\x1b[1mtab %d\x1b[m' % tab)
elif word == 'cd':
cwd = shlex.split(rest)[0]
elif word == 'launch':
args = shlex.split(rest)
data = json.loads(args.pop(0)[len(FLAG):]) if args and args[0].startswith(FLAG) else dict()
wcwd = next((a.partition('=')[2] for a in args if a.startswith('--cwd=')), cwd)
cmd = [a for a in args if not a.startswith('-')] # kitty writes every option as --opt=value
dump, program = '', shlex.join(cmd)
for step in data.get('cmd_at_shell_startup', '').split('; '):
if step.startswith('cat '):
dump = shlex.split(step)[1]
elif step:
program = step
print(' \x1b[36m%s\x1b[m %s' % (wcwd.replace(home, '~', 1), program))
if dump and os.path.exists(dump):
for l in open(dump, errors='replace').read().splitlines()[-6:]:
print(' ' + l + '\x1b[m')
PY
'''
shell = "bash"
# ctrl-e (edit) is bound globally in config.toml.
[keybindings]
enter = "actions:open"
# Today's file, while no running kitty owns it, comes back as the live day
# (saved again); any other day is a read-only snapshot.
[actions.open]
description = "Open the day in a new kitty"
command = "setsid -f kitty --session '{split:\\t:1}' >/dev/null 2>&1"
shell = "bash"
mode = "execute"
[actions.edit]
description = "Open the session file in nvim"
command = "nvim '{split:\\t:1}' </dev/tty >/dev/tty"
shell = "bash"
mode = "execute"
+18
View File
@@ -0,0 +1,18 @@
[metadata]
name = "make-targets"
description = "List and run Makefile targets"
requirements = ["make", "awk"]
[source]
command = "make -pRrq 2>/dev/null | awk -F: '/^[a-zA-Z0-9][^$#\\/\\t=]*:([^=]|$)/ {split($1,a,\" \"); print a[1]}' | sort -u | grep -v '^Makefile$'"
[preview]
command = "awk '/^{}[[:space:]]*:/{found=1} found{print; if(/^[^\\t]/ && NR>1 && !/^{}[[:space:]]*:/) exit}' Makefile"
[keybindings]
enter = "actions:run"
[actions.run]
description = "Run the selected make target"
command = "make {}"
mode = "execute"
+25
View File
@@ -0,0 +1,25 @@
[metadata]
name = "man-pages"
description = "Browse and preview system manual pages"
requirements = ["apropos", "man"]
[source]
# List all man pages using apropos
command = "apropos ."
[preview]
# Show the man page for the selected entry
command = "man '{0}'"
env = { "MANWIDTH" = "80" }
[keybindings]
enter = "actions:open"
[actions.open]
description = "Open the selected man page in the system pager"
command = "man '{0}'"
mode = "execute"
[ui]
layout = "portrait"
preview_panel = { header = "{0}", border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
+19
View File
@@ -0,0 +1,19 @@
[metadata]
name = "mounts"
description = "List mounted filesystems"
requirements = ["df", "awk"]
[source]
command = "df -h --output=target,fstype,size,used,avail,pcent 2>/dev/null | tail -n +2"
display = "{split: :0}"
[preview]
command = "df -h '{}' && echo && ls -la '{}' 2>/dev/null | head -20"
[keybindings]
enter = "actions:cd"
[actions.cd]
description = "Open a shell in the selected mount point"
command = "cd {} && $SHELL"
mode = "execute"
+21
View File
@@ -0,0 +1,21 @@
[metadata]
name = "node-packages"
description = "Browse local node_modules dependencies"
requirements = ["node"]
[source]
command = "ls -d node_modules/*/ 2>/dev/null | sed 's|node_modules/||;s|/$||' | grep -v '^\\.'"
output = "{}"
[preview]
command = "cat 'node_modules/{}/package.json' 2>/dev/null | jq -C '{name, version, description, license, homepage, main}'"
[actions.readme]
description = "View the package README"
command = "cat node_modules/{}/README.md 2>/dev/null | less"
mode = "execute"
[actions.homepage]
description = "Open the package homepage"
command = "node -e \"const p=require('./node_modules/{}/package.json'); const u=p.homepage||'https://www.npmjs.com/package/'+p.name; require('child_process').exec('xdg-open '+u)\""
mode = "fork"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "npm-packages"
description = "List globally installed npm packages"
requirements = ["npm"]
[source]
command = "npm list -g --depth=0 --parseable 2>/dev/null | tail -n +2 | xargs -I{} basename {}"
[preview]
command = "npm info '{}' 2>/dev/null | head -30"
[actions.uninstall]
description = "Uninstall the selected global package"
command = "npm uninstall -g {}"
mode = "execute"
[actions.update]
description = "Update the selected global package"
command = "npm update -g {}"
mode = "execute"
+25
View File
@@ -0,0 +1,25 @@
[metadata]
name = "npm-scripts"
description = "List and run npm scripts from package.json"
requirements = ["jq"]
[source]
command = "jq -r '.scripts | to_entries[] | \"\\(.key)\\t\\(.value)\"' package.json 2>/dev/null"
display = "{split:\\t:0}"
[preview]
command = "jq -r '.scripts[\"{split:\\t:0}\"]' package.json"
[ui.preview_panel]
border_type = "none"
scrollbar = false
padding = { left = 2, right = 1, top = 0, bottom = 0 }
size = 30
[keybindings]
enter = "actions:run"
[actions.run]
description = "Run the selected npm script"
command = "npm run '{split:\\t:0}'"
mode = "execute"
+9
View File
@@ -0,0 +1,9 @@
[metadata]
name = "nu-history"
description = "A channel to select from your nu history"
[source]
command = "nu -c 'open $nu.history-path | lines | uniq | reverse | to text'"
no_sort = true
frecency = false
+66
View File
@@ -0,0 +1,66 @@
[metadata]
name = "obsidian"
description = """
Obsidian vaults under ~/Documents/Obsidian, most recently modified first.
Enter opens the vault in Obsidian; vaults Obsidian has never opened are
registered in obsidian.json first.
"""
requirements = ["fd", "jq", "eza", "obsidian", "xdg-open", "mtime-sort"]
# Entries are "<name>\t<absolute path>": the name is what you fuzzy-match, the
# path is what the actions use.
[source]
command = '''
v=$HOME/Documents/Obsidian
fd -H -t d -d 3 '^\.obsidian$' "$v" -x dirname |
awk -v r="$v/" '{ print substr($0, length(r) + 1) "\t" $0 }' |
mtime-sort
'''
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
[preview]
command = "eza --tree --level 2 --color=always '{split:\\t:1}' 2>/dev/null || ls -la '{split:\\t:1}'"
shell = "bash"
# ctrl-e (edit) is bound globally in config.toml.
[keybindings]
enter = "actions:open"
# obsidian://open?path= only resolves vaults listed in obsidian.json, and
# Obsidian reads that file once at startup and rewrites it from memory. An
# unlisted vault is therefore added only while Obsidian is closed; while it
# runs, the vault switcher opens instead.
[actions.open]
description = "Open the vault in Obsidian"
command = '''
d='{split:\t:1}'
cfg=${XDG_CONFIG_HOME:-$HOME/.config}/obsidian/obsidian.json
uri="obsidian://open?path=$(jq -rn --arg p "$d" '$p | @uri')"
if ! jq -e --arg p "$d" 'any(.vaults[]?; .path == $p)' "$cfg" >/dev/null 2>&1; then
# [o]: keep pgrep from matching this script's own command line.
if pgrep -f '[o]bsidian/app.asar' >/dev/null; then
msg="Vault not registered and Obsidian is running: use 'Open folder as vault' for $d"
echo "$msg" >&2
notify-send Obsidian "$msg" 2>/dev/null
uri="obsidian://choose-vault"
else
mkdir -p "${cfg%/*}"
[ -s "$cfg" ] || echo '{}' >"$cfg"
tmp=$(mktemp "$cfg.XXXXXX") || exit 1
jq --arg id "$(od -An -N8 -tx1 /dev/urandom | tr -d ' \n')" --arg p "$d" \
--argjson ts "$(date +%s%3N)" '.vaults[$id] = {path: $p, ts: $ts}' \
"$cfg" >"$tmp" && mv "$tmp" "$cfg" || rm -f "$tmp"
fi
fi
setsid -f xdg-open "$uri" >/dev/null 2>&1
'''
shell = "bash"
mode = "execute"
[actions.edit]
description = "Open the vault in nvim"
command = "cd '{split:\\t:1}' && nvim . </dev/tty >/dev/tty"
shell = "bash"
mode = "execute"
+38
View File
@@ -0,0 +1,38 @@
[metadata]
name = "omp-sessions"
description = """
omp sessions of the current git repo, newest first: age, the branch HEAD was
on when the session was last written, and the title. The preview is the
conversation's message text; enter resumes the session, ctrl-n starts a new
one instead. Listing, preview, resume and new live in ~/.local/bin/omp-session,
whose launcher (ctrl-a in fish and zsh) opens this cable when it has no
session to resume on the default branch.
"""
requirements = ["omp-session", "omp"]
# Record: display, session file (tab separated).
[source]
command = "omp-session --list"
shell = "bash"
display = "{split:\\t:0}"
output = "{split:\\t:1}"
[preview]
command = "omp-session --preview '{split:\\t:1}'"
shell = "bash"
[keybindings]
enter = "actions:resume"
ctrl-n = "actions:new"
[actions.resume]
description = "Resume the session"
command = "omp-session --resume '{split:\\t:1}'"
shell = "bash"
mode = "execute"
[actions.new]
description = "Start a new omp session"
command = "omp-session --new"
shell = "bash"
mode = "execute"
+38
View File
@@ -0,0 +1,38 @@
[metadata]
name = "opencode-sessions"
description = "Browse and resume OpenCode sessions"
requirements = ["opencode", "jq"]
[source]
# Use literal strings (''') to avoid escape sequence errors
command = '''opencode session list --format json | jq -r '.[] | "\(.id)\t\(.title)\t\(.directory)"' '''
display = "{split:\t:1} ({split:\t:2})"
output = "{split:\t:0}"
[preview]
# We use triple single quotes so we don't have to escape the backslashes for jq or the template engine
command = '''opencode session list --format json | jq -r '.[] | select(.id == "{split:\t:0}") | "TITLE: \(.title)\nID: \(.id)\nPROJECT: \(.projectId)\nDIRECTORY: \(.directory)\nUPDATED: \((.updated / 1000) | strftime("%Y-%m-%d %H:%M:%S"))"' '''
[ui]
layout = "landscape"
[ui.preview_panel]
size = 60
header = "Session: {split:\t:1}"
border_type = "none"
scrollbar = false
padding = { left = 2, right = 1, top = 0, bottom = 0 }
[keybindings]
shortcut = "f4"
enter = "actions:resume"
ctrl-d = "actions:delete"
[actions.resume]
description = "Resume session"
command = "opencode -s {split:\t:0}"
mode = "execute"
[actions.delete]
description = "Delete session"
command = '''bash -c 'echo "Delete {split:\t:1}?" && read -p "[y/N]: " conf && [[ $conf == "y" ]] && opencode session delete {split:\t:0} ' '''
mode = "execute"
+21
View File
@@ -0,0 +1,21 @@
[metadata]
name = "pacman-packages"
description = "List and manage installed pacman packages"
requirements = ["pacman"]
[source]
command = "pacman -Q"
output = "{split: :0}"
[preview]
command = "pacman -Qi '{split: :0}' 2>/dev/null"
[actions.remove]
description = "Remove the selected package"
command = "sudo pacman -R '{split: :0}'"
mode = "execute"
[actions.files]
description = "List files owned by the selected package"
command = "pacman -Ql '{split: :0}' | less"
mode = "execute"
+15
View File
@@ -0,0 +1,15 @@
[metadata]
name = "path"
description = "Investigate PATH contents"
requirements = ["fd", "bat"]
[source]
command = "printf '%s\n' \"$PATH\" | tr ':' '\n'"
[preview]
command = "fd -tx -d1 . \"{}\" -X printf \"%s\n\" \"{/}\" | sort -f | bat -n --color=always"
[actions.cd]
description = "Open a shell in the selected PATH directory"
command = "cd {} && $SHELL"
mode = "execute"
+24
View File
@@ -0,0 +1,24 @@
[metadata]
name = "pdf-files"
description = "Browse PDF files"
requirements = ["fd", "pdftotext"]
[source]
command = [
{ name = "Default", run = "fd -t f -e pdf ." },
{ name = "Hidden", run = "fd -t f -e pdf -H ." },
]
[preview]
command = "pdftotext -l 2 -layout '{}' - 2>/dev/null | head -100 || file '{}'"
[ui]
layout = "portrait"
[keybindings]
enter = "actions:open"
[actions.open]
description = "Open the selected PDF with default viewer"
command = "xdg-open {} 2>/dev/null || open {}"
mode = "fork"
+27
View File
@@ -0,0 +1,27 @@
[metadata]
name = "pip-packages"
description = "List installed Python packages"
requirements = ["pip"]
[source]
command = "pip list --format=freeze 2>/dev/null | cut -d= -f1"
[preview]
command = "pip show '{}'"
[ui]
layout = "portrait"
[keybindings]
ctrl-u = "actions:upgrade"
ctrl-d = "actions:uninstall"
[actions.upgrade]
description = "Upgrade the selected package"
command = "pip install --upgrade {}"
mode = "execute"
[actions.uninstall]
description = "Uninstall the selected package"
command = "pip uninstall {}"
mode = "execute"
+29
View File
@@ -0,0 +1,29 @@
[metadata]
name = "pj"
description = """
Project tasks from every task source between the cwd and the git toplevel
(package.json, Makefile, justfile, mise, devbox, pyproject), tasks of the most
recently modified manifest first. Enter runs the task. Discovery, preview and
run live in ~/.local/bin/pj; `pj` (ctrl-p, and down on an empty prompt in fish,
zsh and bash) opens this cable with its own scan.
"""
requirements = ["pj", "mtime-sort"]
# Record: display, kind, target, root, runner, manifest (tab separated).
[source]
command = "pj --list 2>/dev/null"
shell = "bash"
display = "{split:\\t:0}"
[preview]
command = "pj --preview '{split:\\t:1}' '{split:\\t:2}' '{split:\\t:3}' '{split:\\t:5}'"
shell = "bash"
[keybindings]
enter = "actions:run"
[actions.run]
description = "Run the selected task"
command = "pj --run '{split:\\t:1}' '{split:\\t:2}' '{split:\\t:3}' '{split:\\t:4}'"
shell = "bash"
mode = "execute"
+22
View File
@@ -0,0 +1,22 @@
[metadata]
name = "ports"
description = "List listening ports and associated processes"
requirements = ["ss", "awk"]
[source]
command = "ss -tlnp 2>/dev/null | tail -n +2 | awk '{gsub(/.*:/,\"\",$4); print $4, $1, $6}' | sed 's/users:((\"//; s/\".*//'"
display = "{split: :0} ({split: :2})"
[preview]
command = "ss -tlnp 2>/dev/null | grep ':{split: :0} ' | head -20"
[ui.preview_panel]
border_type = "none"
scrollbar = false
padding = { left = 2, right = 1, top = 0, bottom = 0 }
size = 40
[actions.kill]
description = "Kill the process listening on the selected port"
command = "fuser -k {split: :0}/tcp"
mode = "execute"
+38
View File
@@ -0,0 +1,38 @@
[metadata]
name = "procs"
description = "A channel to find and manage running processes"
requirements = ["ps", "awk"]
[source]
command = "ps -e -o pid=,ucomm= | awk '{print $1, $2}'"
display = "{split: :1}"
output = "{split: :0}"
[preview]
command = "ps -p '{split: :0}' -o user,pid,ppid,state,%cpu,%mem,command | fold"
[keybindings]
ctrl-k = "actions:kill"
f2 = "actions:term"
ctrl-s = "actions:stop"
ctrl-c = "actions:cont"
[actions.kill]
description = "Kill the selected process (SIGKILL)"
command = "kill -9 {split: :0}"
mode = "execute"
[actions.term]
description = "Terminate the selected process (SIGTERM)"
command = "kill -15 {split: :0}"
mode = "execute"
[actions.stop]
description = "Stop/pause the selected process (SIGSTOP)"
command = "kill -STOP {split: :0}"
mode = "fork"
[actions.cont]
description = "Continue/resume the selected process (SIGCONT)"
command = "kill -CONT {split: :0}"
mode = "fork"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "python-venvs"
description = "Find Python virtual environments"
requirements = ["find"]
[source]
command = "find ~ -maxdepth 5 -type f -name 'pyvenv.cfg' 2>/dev/null | xargs -I{} dirname {}"
[preview]
command = "cat '{}/pyvenv.cfg' 2>/dev/null && echo '' && echo 'Packages:' && '{}/bin/pip' list --format=columns 2>/dev/null | head -20"
[actions.activate]
description = "Open a shell with the selected venv activated"
command = "source '{}/bin/activate' && $SHELL"
mode = "execute"
[actions.packages]
description = "List all packages in the selected venv"
command = "'{}/bin/pip' list | less"
mode = "execute"
+23
View File
@@ -0,0 +1,23 @@
[metadata]
name = "recent-files"
description = "List recently modified files (via git or filesystem)"
requirements = ["git", "bat"]
[source]
command = [
{ name = "Recent", run = "git diff --name-only HEAD~10 HEAD 2>/dev/null || find . -type f -mtime -7 -not -path '*/.*' 2>/dev/null | head -100" },
{ name = "Last hour", run = "find . -type f -mmin -60 -not -path '*/.*' 2>/dev/null | head -100" },
]
[preview]
command = "bat -n --color=always '{}'"
env = { BAT_THEME = "ansi" }
[keybindings]
enter = "actions:edit"
[actions.edit]
description = "Open the selected file in editor"
command = "${EDITOR:-vim} {}"
shell = "bash"
mode = "execute"
+20
View File
@@ -0,0 +1,20 @@
[metadata]
name = "rustup"
description = "Manage Rust toolchains"
requirements = ["rustup"]
[source]
command = "rustup toolchain list"
[preview]
command = "rustup run '{split: :0}' rustc --version --verbose 2>/dev/null"
[actions.default]
description = "Set the selected toolchain as default"
command = "rustup default '{split: :0}'"
mode = "execute"
[actions.uninstall]
description = "Uninstall the selected toolchain"
command = "rustup toolchain uninstall '{split: :0}'"
mode = "execute"
+18
View File
@@ -0,0 +1,18 @@
[metadata]
name = "ssh-hosts"
description = "A channel to select hosts from your SSH config"
requirements = ["grep", "tr", "cut", "awk"]
[source]
command = "grep -E '^Host(name)? ' $HOME/.ssh/config | tr -s ' ' | cut -d' ' -f2- | tr ' ' '\n' | grep -v '^$'"
[preview]
command = "awk '/^Host / { found=0 } /^Host (.*[[:space:]])?'{}'([[:space:]].*)?$/ { found=1 } found' $HOME/.ssh/config"
[keybindings]
enter = "actions:connect"
[actions.connect]
description = "SSH into the selected host"
command = "ssh {}"
mode = "execute"
+49
View File
@@ -0,0 +1,49 @@
[metadata]
name = "systemd-units"
description = "List and manage systemd services"
requirements = ["systemctl"]
[source]
command = [
{ name = "Active", run = "systemctl list-units --type=service --no-pager --no-legend --plain" },
{ name = "All", run = "systemctl list-units --type=service --all --no-pager --no-legend --plain" },
]
display = "{split: :0}"
[preview]
command = "systemctl status '{split: :0}' --no-pager"
[ui]
layout = "portrait"
[keybindings]
ctrl-s = "actions:start"
f2 = "actions:stop"
ctrl-r = "actions:restart"
ctrl-e = "actions:enable"
ctrl-d = "actions:disable"
[actions.start]
description = "Start the selected service"
command = "sudo systemctl start '{split: :0}'"
mode = "execute"
[actions.stop]
description = "Stop the selected service"
command = "sudo systemctl stop '{split: :0}'"
mode = "execute"
[actions.restart]
description = "Restart the selected service"
command = "sudo systemctl restart '{split: :0}'"
mode = "execute"
[actions.enable]
description = "Enable the selected service"
command = "sudo systemctl enable '{split: :0}'"
mode = "execute"
[actions.disable]
description = "Disable the selected service"
command = "sudo systemctl disable '{split: :0}'"
mode = "execute"
+18
View File
@@ -0,0 +1,18 @@
[metadata]
name = "tailscale-exit-node"
description = "A channel to select a tailscale exit node"
requirements = ["tailscale", "awk"]
[source]
command = "tailscale exit-node list | awk '/^[[:space:]]*[0-9]+\\./ {gsub(/^[[:space:]]+/, \"\"); print $1}'"
[preview]
command = "tailscale whois {}"
[keybindings]
enter = "actions:connect"
[actions.connect]
description = "Set selected host as exit node"
command = "tailscale set --exit-node={}"
mode = "execute"
+36
View File
@@ -0,0 +1,36 @@
[metadata]
name = "tea-issues"
description = "List Gitea issues for the current repo"
requirements = ["tea", "jq"]
[source]
command = '''tea issues list --output json --state open </dev/null | jq -r '.[] | "\u001b[32m#\(.index)\u001b[39m" + "\t\(.title) \u001b[33m@\(.author)\u001b[39m" + (if .labels != "" then " \u001b[35m" + .labels + "\u001b[39m" else "" end)'
'''
ansi = true
output = "{strip_ansi|split:#:1|split:\t:0}"
[ui]
layout = "portrait"
preview_panel = { header = '{strip_ansi|split:\t:1}', border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[preview]
command = '''tea issues --output json {strip_ansi|split:#:1|split:\t:0} </dev/null | jq -r '
.title,
"#" + (.index | tostring),
"",
"\u001b[36mStatus:\u001b[39m \u001b[32m" + .state + "\u001b[39m",
"\u001b[36mAuthor:\u001b[39m \u001b[33m" + .user + "\u001b[39m",
"\u001b[36mCreated:\u001b[39m " + .created[:10],
(if (.labels | length) > 0 then "\u001b[36mLabels:\u001b[39m " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"]
| join(" ")) else empty end),
"",
"\u001b[90m────────────────────────────────────────────────────────────\u001b[39m",
"",
(.body // "")
'
'''
[actions.close]
description = "Close the selected issue"
command = "tea issues close {strip_ansi|split:#:1|split:\t:0}"
mode = "fork"
+42
View File
@@ -0,0 +1,42 @@
[metadata]
name = "tea-prs"
description = "List Gitea PRs for the current repo"
requirements = ["tea", "jq"]
[source]
command = '''tea pulls list --output json --state open </dev/null | jq -r '.[] | "\u001b[32m#\(.index)\u001b[39m" + "\t\(.title) \u001b[33m@\(.author)\u001b[39m" + (if .labels != "" then " \u001b[35m" + .labels + "\u001b[39m" else "" end)'
'''
ansi = true
output = "{strip_ansi|split:#:1|split:\t:0}"
[ui]
layout = "portrait"
preview_panel = { header = '{strip_ansi|split:\t:1}', border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[preview]
command = '''tea pulls --output json {strip_ansi|split:#:1|split:\t:0} </dev/null | jq -r '
.title,
"#" + (.index | tostring),
"",
"\u001b[36mStatus:\u001b[39m \u001b[32m" + .state + "\u001b[39m " + .base + " ← " + .head,
"\u001b[36mAuthor:\u001b[39m \u001b[33m" + .user + "\u001b[39m",
"\u001b[36mCreated:\u001b[39m " + .created[:10],
"\u001b[36mUpdated:\u001b[39m " + .updated[:10],
(if (.labels | length) > 0 then "\u001b[36mLabels:\u001b[39m " + ([.labels[] | "\u001b[35m" + .name + "\u001b[39m"] | join(" ")) else empty end),
"\u001b[36mMergeable:\u001b[39m " + (if .mergeable then "\u001b[32m✓ Clean\u001b[39m" else "\u001b[31m✗ Dirty\u001b[39m" end),
"",
"\u001b[90m────────────────────────────────────────────────────────────\u001b[39m",
"",
(.body // "")
'
'''
[actions.checkout]
description = "Checkout the PR branch locally"
command = "tea pulls checkout {strip_ansi|split:#:1|split:\t:0}"
mode = "execute"
[actions.merge]
description = "Merge the selected PR"
command = "tea pulls merge {strip_ansi|split:#:1|split:\t:0}"
mode = "execute"
+29
View File
@@ -0,0 +1,29 @@
[metadata]
name = "text"
description = "A channel to find and select text from files"
requirements = ["rg", "bat"]
[source]
command = [
{ name = "Default", run = "rg . --no-heading --line-number --colors 'match:fg:white' --colors 'path:fg:blue' --color=always" },
{ name = "Hidden", run = "rg . --no-heading --line-number --hidden --colors 'match:fg:white' --colors 'path:fg:blue' --color=always" },
]
ansi = true
output = "{strip_ansi|split:\\::..2}"
[preview]
command = "bat -n --color=always '{strip_ansi|split:\\::0}'"
env = { BAT_THEME = "ansi" }
offset = '{strip_ansi|split:\::1}'
[ui]
preview_panel = { header = '{strip_ansi|split:\::..2}', border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[keybindings]
enter = "actions:edit"
[actions.edit]
description = "Open file in editor at line"
command = "${EDITOR:-vim} '+{strip_ansi|split:\\::1}' '{strip_ansi|split:\\::0}'"
shell = "bash"
mode = "execute"
+21
View File
@@ -0,0 +1,21 @@
[metadata]
name = "tldr"
description = "Browse and preview TLDR help pages for command-line tools"
requirements = ["tldr"]
[source]
command = "tldr --list"
[preview]
command = "tldr '{0}' --color always"
[ui]
layout = "portrait"
[keybindings]
ctrl-e = "actions:open"
[actions.open]
description = "Open the selected TLDR page"
command = "tldr '{0}'"
mode = "execute"
+22
View File
@@ -0,0 +1,22 @@
[metadata]
name = "tmux-sessions"
description = "List and manage tmux sessions"
requirements = ["tmux"]
[source]
command = "tmux list-sessions -F '#{session_name}\t#{session_windows} windows\t#{session_created_string}'"
display = "{split:\t:0} ({split:\t:1})"
output = "{split:\t:0}"
[preview]
command = "tmux capture-pane -t '{split:\t:0}' -p 2>/dev/null || echo 'No preview available'"
[actions.attach]
description = "Attach to the selected session"
command = "tmux attach-session -t '{split:\t:0}'"
mode = "execute"
[actions.kill]
description = "Kill the selected session"
command = "tmux kill-session -t '{split:\t:0}'"
mode = "fork"
+22
View File
@@ -0,0 +1,22 @@
[metadata]
name = "tmux-windows"
description = "List and switch between tmux windows"
requirements = ["tmux"]
[source]
command = "tmux list-windows -a -F '#{session_name}:#{window_index}\t#{window_name}\t#{pane_current_command}'"
display = "{split:\t:0} - {split:\t:1} ({split:\t:2})"
output = "{split:\t:0}"
[preview]
command = "tmux capture-pane -t '{split:\t:0}' -p 2>/dev/null || echo 'No preview available'"
[actions.select]
description = "Switch to the selected window"
command = "tmux select-window -t '{split:\t:0}'"
mode = "execute"
[actions.kill]
description = "Kill the selected window"
command = "tmux kill-window -t '{split:\t:0}'"
mode = "fork"
+26
View File
@@ -0,0 +1,26 @@
[metadata]
name = "todo-comments"
description = "Find TODO, FIXME, HACK, and XXX comments in codebase"
requirements = ["rg", "bat"]
[source]
command = "rg -n --color=always '(TODO|FIXME|HACK|XXX|BUG|WARN):?'"
ansi = true
output = "{strip_ansi|split:\\::..2}"
[preview]
command = "bat -n --color=always --highlight-line '{strip_ansi|split:\\::1}' '{strip_ansi|split:\\::0}'"
env = { BAT_THEME = "ansi" }
offset = "{strip_ansi|split:\\::1}"
[ui]
preview_panel = { header = "{strip_ansi|split:\\::..2}", border_type = "none", scrollbar = false, padding = { left = 2, right = 1, top = 0, bottom = 0 } }
[keybindings]
enter = "actions:edit"
[actions.edit]
description = "Open file in editor at the comment"
command = "${EDITOR:-vim} '+{strip_ansi|split:\\::1}' '{strip_ansi|split:\\::0}'"
shell = "bash"
mode = "execute"
+43
View File
@@ -0,0 +1,43 @@
[metadata]
name = "unicode"
description = '''Search and insert unicode characters
The UnicodeData.txt file is included by many packages.
In addition to:
Alpine Linux: unicode-character-database
Arch: unicode-character-database
Debian/Ubuntu: unicode-data
Fedora / RHEL / CentOS unicode-ucd
Gentoo: app-i18n/unicode-data
NixOS: unicode/unicode-data
openSUSE: unicode-ucd
UnicodData.txt may also aleady be provided by:
1) Many java packages
2) Latex packages
3) Still others
It may in some cases be necessary to alter UNICODE_FILE below.
'''
requirements = ["awk", "perl"]
[source]
command = '''
UNICODE_FILE="/usr/share/unicode/ucd/UnicodeData.txt"
awk -F';' '
$2 !~ /^</ { print $1 "|" $2 }
' "$UNICODE_FILE" \
| perl -CS -F'\|' -lane '
$code = $F[0];
$desc = $F[1];
$char = chr(hex($code));
print "U+$code|$char|$desc" if $char =~ /\p{Print}/;
'
'''
display = "{split:|:0} {split:|:1} {split:|:2}"
output = "{split:|:1}"
+17
View File
@@ -0,0 +1,17 @@
[metadata]
name = "wifi"
description = "Scan and connect to WiFi networks"
requirements = ["nmcli"]
[source]
command = "nmcli -t -f SSID,SIGNAL,SECURITY device wifi list 2>/dev/null | grep -v '^:' | sort -t: -k2 -rn"
display = "{split:\\::0} ({split:\\::1}% {split:\\::2})"
output = "{split:\\::0}"
[preview]
command = "nmcli -t -f SSID,BSSID,MODE,FREQ,SIGNAL,SECURITY,ACTIVE device wifi list 2>/dev/null | grep '^{split:\\::0}:'"
[actions.connect]
description = "Connect to the selected network"
command = "nmcli device wifi connect '{split:\\::0}'"
mode = "execute"
+26
View File
@@ -0,0 +1,26 @@
[metadata]
name = "zoxide"
description = "Browse zoxide directory history"
requirements = ["zoxide"]
[source]
command = "zoxide query -l"
no_sort = true
frecency = false
[preview]
command = "ls -la --color=always '{}'"
[keybindings]
enter = "actions:cd"
ctrl-d = "actions:remove"
[actions.cd]
description = "Change to the selected directory"
command = "cd {} && $SHELL"
mode = "execute"
[actions.remove]
description = "Remove the selected directory from zoxide"
command = "zoxide remove {}"
mode = "fork"
+16
View File
@@ -0,0 +1,16 @@
[metadata]
name = "zsh-history"
description = "A channel to select from your zsh history"
requirements = ["zsh"]
[source]
command = "sed '1!G;h;$!d' ${HISTFILE:-${HOME}/.zsh_history}"
display = "{split:;:1..}"
output = "{split:;:1..}"
no_sort = true
frecency = false
[actions.execute]
description = "Execute the selected command"
command = "zsh -c '{split:;:1..}'"
mode = "execute"