mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 11:32:42 +00:00
chs: re-add drifted files
This commit is contained in:
1 parent
3ba7b5fb55
commit
31ff6c0fa6
30 files changed
+742
-27
No files matched your search
@@ -131,9 +131,9 @@
|
||||
"sourceType": "github",
|
||||
"sourceUrl": "https://github.com/vercel-labs/json-render.git",
|
||||
"skillPath": "skills/react/SKILL.md",
|
||||
"skillFolderHash": "de4cf00b0cdcdfab52fe03fb7a832d6f18a9575a",
|
||||
"skillFolderHash": "9c588f4ee3abe16fc959c4ddea65f43c786edc2c",
|
||||
"installedAt": "2026-03-13T21:55:40.915Z",
|
||||
"updatedAt": "2026-08-14T01:06:47.841Z"
|
||||
"updatedAt": "2026-08-15T20:19:34.630Z"
|
||||
},
|
||||
"react-pdf": {
|
||||
"source": "vercel-labs/json-render",
|
||||
@@ -347,9 +347,9 @@
|
||||
"sourceType": "github",
|
||||
"sourceUrl": "https://github.com/remix-run/remix.git",
|
||||
"skillPath": ".agents/skills/remix/SKILL.md",
|
||||
"skillFolderHash": "b83968c33a93974421b86f9b70dae71b68414b25",
|
||||
"skillFolderHash": "f4a5b113c626d7b17834af0bad6bfdb6b41cbf0f",
|
||||
"installedAt": "2026-05-23T19:11:24.642Z",
|
||||
"updatedAt": "2026-08-14T01:06:55.674Z"
|
||||
"updatedAt": "2026-08-15T20:19:36.456Z"
|
||||
},
|
||||
"typescript-expert": {
|
||||
"source": "remix-run/remix",
|
||||
|
||||
@@ -119,6 +119,7 @@ Any prop value can be a data-driven expression resolved by the renderer before c
|
||||
- **`{ "$bindState": "/path" }`** - two-way binding: reads from state and enables write-back. Use on the natural value prop (value, checked, pressed, etc.) of form components.
|
||||
- **`{ "$bindItem": "field" }`** - two-way binding to a repeat item field. Use inside repeat scopes.
|
||||
- **Filtered lists**: `repeat` plus an `$item` visible condition on the same container renders only matching items: `{ "repeat": { "statePath": "/tasks", "key": "id" }, "visible": { "$item": "status", "eq": "todo" }, "children": ["task-card"] }`. AND-composed `$state` conjuncts gate the container shell; `$item`/`$index` conjuncts filter items.
|
||||
- **Nested lists**: inside a repeat, use `{ "repeat": { "statePath": { "$item": "comments" }, "key": "id" } }` to iterate an array on the enclosing item.
|
||||
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - conditional value
|
||||
- **`{ "$template": "Hello, ${/name}!" }`** - interpolates state values into strings
|
||||
- **`{ "$computed": "fn", "args": { ... } }`** - calls registered functions with resolved args
|
||||
|
||||
@@ -84,7 +84,7 @@ let entryPreloads = await assetServer.getPreloads('app/actions/public/entry.ts')
|
||||
|
||||
Use this when rendering documents or layouts that boot browser behavior with a known client entry.
|
||||
|
||||
When resolving hydrated client entries during server rendering, pass the source entry ID from `clientEntry(import.meta.url, ...)` to `getHref()` inside `resolveClientEntry`. Keep export-name resolution in that render helper, and avoid hard-coding public asset URLs in source-owned component modules.
|
||||
When resolving hydrated client entries during server rendering, pass the source entry ID from `clientEntry(import.meta.url, ...)` to `getHref()` and `getPreloads()` inside `resolveClientEntry`. Return those preload hrefs through the entry's `preloads` property so the browser can fetch the module graph without walking it serially. Keep this resolution in the shared render helper rather than hard-coding public asset URLs in source-owned component modules.
|
||||
|
||||
## Development vs Deployment
|
||||
|
||||
|
||||
@@ -76,9 +76,15 @@ let stream = renderToStream(<App />, {
|
||||
throw new Error(`Unable to resolve client entry export for ${entryId}`)
|
||||
}
|
||||
|
||||
let [href, preloads] = await Promise.all([
|
||||
assetServer.getHref(entryId),
|
||||
assetServer.getPreloads(entryId),
|
||||
])
|
||||
|
||||
return {
|
||||
href: await assetServer.getHref(entryId),
|
||||
href,
|
||||
exportName,
|
||||
preloads,
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -90,6 +96,8 @@ On the server, `clientEntry` components render like any other component. The ser
|
||||
|
||||
Client entry props must be serializable: strings, numbers, booleans, `null`, `undefined`, plain objects/arrays of the above, JSX elements, and `<Frame>` elements. Functions and class instances cannot be passed.
|
||||
|
||||
The resolved `preloads` array contains browser module hrefs. During server rendering these are emitted as `<link rel="modulepreload">` tags, including preloads discovered in blocking frames. When a later frame response introduces a client entry, its preloads start before the entry module is loaded.
|
||||
|
||||
## Booting the Client
|
||||
|
||||
Use `run` to start the client runtime. It scans the document for client entry markers, loads modules, and hydrates each one:
|
||||
|
||||
+35
-6
@@ -1,13 +1,42 @@
|
||||
# Aliases — generated from .chezmoidata/aliases.yaml (shared with bash/zsh
|
||||
# via ~/.config/shell/aliases.sh). Add shell-agnostic aliases in the YAML;
|
||||
# fish-only bits live below the generated block.
|
||||
{{- range .aliases }}
|
||||
|
||||
# {{ .section }}
|
||||
{{- range .items }}
|
||||
alias {{ .name }} {{ if contains " " .cmd }}'{{ .cmd }}'{{ else }}{{ .cmd }}{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
# Editors / file managers
|
||||
alias e nvim
|
||||
alias e.d 'cd ~/.config/dotstate/storage/Rogz/.config && yazi'
|
||||
alias e.f 'cd ~/.config/fish && yazi'
|
||||
alias e.k 'yazi ~/.config/kitty'
|
||||
alias e.n 'yazi ~/.config/niri'
|
||||
alias e.v 'yazi ~/.config/nvim/lua'
|
||||
alias e.y 'yazi ~/.config/yazi'
|
||||
alias e.t 'nvim ~/.config/tmux/tmux.conf'
|
||||
alias hx helix
|
||||
|
||||
# Quick directory jumps
|
||||
alias cdc 'cd ~/.config && yazi'
|
||||
alias cdd 'cd ~/Developer && yazi'
|
||||
alias cdk 'cd ~/Documents/Knowledge && obsidian'
|
||||
alias cdl 'cd ~/.local && yazi'
|
||||
alias cds 'cd ~/.local/share/services && ls'
|
||||
alias cdw 'cd ~/Downloads && yazi'
|
||||
|
||||
# Tools
|
||||
alias dd lazydocker
|
||||
alias gg lazygit
|
||||
alias ff 'exec fish'
|
||||
alias npx bunx
|
||||
alias sm 'sudo micro'
|
||||
alias sd 'ssh prad@192.168.1.68'
|
||||
alias oc opencode
|
||||
alias gop 'gh repo view --web'
|
||||
alias dss 'dotstate sync'
|
||||
alias fabric fabric-ai
|
||||
alias bk bead-kanban
|
||||
alias sua 'shelly upgrade-all -n'
|
||||
alias lc leetcode
|
||||
alias tk task
|
||||
alias .. 'cd ..'
|
||||
|
||||
# Clipboard
|
||||
if command -v wl-copy >/dev/null 2>&1
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
source "$HOME/.atuin/bin/env.fish"
|
||||
@@ -0,0 +1,4 @@
|
||||
# >>> oh-my-opencode-slim background subagents >>>
|
||||
set -gx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true
|
||||
set -gx OPENCODE_ENABLE_EXA 1
|
||||
# <<< oh-my-opencode-slim background subagents <<<
|
||||
@@ -0,0 +1,27 @@
|
||||
# Config is split into autoloaded files (see `man fish` → Configuration files):
|
||||
# conf.d/00-path.fish PATH entries
|
||||
# conf.d/10-env.fish environment + fish_greeting + fzf theme
|
||||
# conf.d/20-aliases.fish aliases
|
||||
# conf.d/40-tools.fish starship / zoxide / mise / hishtory init
|
||||
# conf.d/50-terminal-title.fish tmux window title
|
||||
# conf.d/secrets.fish API keys (gitignored)
|
||||
# functions/ one autoloaded function per file
|
||||
# functions/fish_user_key_bindings.fish key bindings
|
||||
#
|
||||
# Nothing needs to live here; keep it empty so startup stays fast.
|
||||
|
||||
function starship_transient_prompt_func
|
||||
starship module character
|
||||
end
|
||||
|
||||
function starship_transient_rprompt_func
|
||||
starship module time
|
||||
end
|
||||
|
||||
starship init fish | source
|
||||
enable_transience
|
||||
|
||||
# Pin the prompt to the bottom of the terminal (FISH_BOTTOM_PROMPT=1, the
|
||||
# default from conf.d/10-env.fish; set 0 for the normal top prompt).
|
||||
# functions/clear.fish and the ctrl-l binding re-pin it after clearing.
|
||||
status is-interactive; and test "$FISH_BOTTOM_PROMPT" = 1; and tput cup $LINES 0
|
||||
@@ -0,0 +1,13 @@
|
||||
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
|
||||
@@ -0,0 +1,3 @@
|
||||
function c2p --description "Code2Prompt with fuzzy-search template"
|
||||
fd --type f --extension hbs . $HOME/.config/code2prompt | fzf | xargs code2prompt --template
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
function cp-appid --description "Fuzzy-pick a desktop app ID and copy to clipboard"
|
||||
set -l search_dirs \
|
||||
/usr/share/applications \
|
||||
/usr/local/share/applications \
|
||||
$HOME/.local/share/applications \
|
||||
/var/lib/flatpak/exports/share/applications \
|
||||
$HOME/.local/share/flatpak/exports/share/applications \
|
||||
/var/lib/snapd/desktop/applications
|
||||
|
||||
set -l existing_dirs
|
||||
for d in $search_dirs
|
||||
test -d $d; and set -a existing_dirs $d
|
||||
end
|
||||
|
||||
test (count $existing_dirs) -eq 0
|
||||
and echo "No application directories found." >&2
|
||||
and return 1
|
||||
|
||||
set -l chosen (
|
||||
fd --type f --extension desktop . $existing_dirs \
|
||||
| while read -l f
|
||||
set -l app_id (basename $f .desktop)
|
||||
set -l name (rg -m1 '^Name=' $f | string replace 'Name=' '')
|
||||
test -z "$name"; and set name $app_id
|
||||
echo "$name\t$app_id"
|
||||
end \
|
||||
| sort -u -t\t -k1 \
|
||||
| fzf \
|
||||
--delimiter='\t' \
|
||||
--with-nth=1,2 \
|
||||
--prompt="App ID › " \
|
||||
--height=50% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--info=inline
|
||||
)
|
||||
|
||||
test -z "$chosen"; and return 0
|
||||
|
||||
set -l app_id (string split '\t' $chosen)[2]
|
||||
echo -n $app_id | wl-copy
|
||||
echo "Copied: $app_id"
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
function czs --description 'chezmoi: re-add drifted files, then commit + push the source repo'
|
||||
# Optional targets scope both the status view and the re-add,
|
||||
# e.g. `chs ~/.config/fish`. No args = all managed files.
|
||||
set -l drift (chezmoi status $argv)
|
||||
or return
|
||||
|
||||
if test -n "$drift"
|
||||
printf '%s\n' $drift
|
||||
# re-add takes the local (destination) contents as truth for modified
|
||||
# files; templates are never overwritten, non-file entries are ignored.
|
||||
chezmoi re-add $argv
|
||||
or return
|
||||
else
|
||||
echo "chs: in sync — nothing to re-add"
|
||||
end
|
||||
|
||||
# Commit + sync the source repo (~/.local/share/chezmoi).
|
||||
set -l src (chezmoi source-path)
|
||||
if test -n "$(git -C $src status --porcelain)"
|
||||
git -C $src add -A
|
||||
and git -C $src commit --quiet -m 'chs: re-add drifted files'
|
||||
or return
|
||||
echo "chs: committed "(git -C $src rev-parse --short HEAD)
|
||||
end
|
||||
|
||||
# Push anything unpushed — covers this commit AND leftovers from a
|
||||
# previously failed push. --autostash tolerates post-commit hooks
|
||||
# (beads export) re-dirtying the tree mid-sync.
|
||||
set -l ahead (git -C $src rev-list --count @{upstream}..HEAD 2>/dev/null; or echo 0)
|
||||
if test $ahead -gt 0
|
||||
git -C $src pull --rebase --autostash --quiet
|
||||
and git -C $src push --quiet
|
||||
and echo "chs: pushed $ahead commit(s) to "(git -C $src remote get-url origin)
|
||||
else
|
||||
echo "chs: remote up to date"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
function extract_agents -d "Copy all AGENTS.md files to a target directory while preserving structure"
|
||||
set -l out_dir $argv[1]
|
||||
|
||||
# Validate input
|
||||
if test -z "$out_dir"
|
||||
echo "Error: Output directory required."
|
||||
echo "Usage: extract_agents <absolute_or_relative_output_path>"
|
||||
return 1
|
||||
end
|
||||
|
||||
# Ensure output directory exists
|
||||
mkdir -p $out_dir
|
||||
|
||||
# Find and copy preserving relative paths
|
||||
fd -t f -g "AGENTS.md" -x cp --parents "{}" $out_dir
|
||||
|
||||
echo "Extraction complete. Check $out_dir."
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
function fab -d "Fuzzy find and execute a Fabric pattern"
|
||||
set -l json_file ~/.config/fabric/pattern_explainations.json
|
||||
|
||||
if not test -f $json_file
|
||||
set_color red; echo "Error: $json_file not found."; set_color normal
|
||||
return 1
|
||||
end
|
||||
|
||||
# 1. Safely capture piped input (if any) to a temp file
|
||||
# This preserves all formatting, newlines, and avoids variable escaping issues
|
||||
set -l temp_input (mktemp)
|
||||
if not isatty stdin
|
||||
cat > $temp_input
|
||||
end
|
||||
|
||||
# 2. Parse JSON to TSV -> fzf (Name in list, Description in preview) -> Extract Name
|
||||
set -l selection (jq -r '.[] | "\(.name)\t\(.description)"' $json_file | \
|
||||
fzf --delimiter '\t' \
|
||||
--with-nth 1 \
|
||||
--preview 'echo {2}' \
|
||||
--preview-window 'right:60%:wrap' \
|
||||
--prompt 'Pattern > ' \
|
||||
--info inline \
|
||||
--bind 'ctrl-/:toggle-preview')
|
||||
|
||||
# 3. Execute with or without the piped data
|
||||
if test -n "$selection"
|
||||
set -l pattern (string split -f 1 \t "$selection")
|
||||
|
||||
# If our temp file has data (-s), pipe it. Otherwise, run bare.
|
||||
if test -s $temp_input
|
||||
cat $temp_input | fabric --pattern "$pattern"
|
||||
else
|
||||
fabric --pattern "$pattern"
|
||||
end
|
||||
end
|
||||
|
||||
# 4. Clean up
|
||||
rm -f $temp_input
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
function gdc --description "Discard all git changes with gum confirmation"
|
||||
if gum confirm "Discard all uncommitted changes? This is irreversible."
|
||||
git reset --hard HEAD
|
||||
git clean -fd
|
||||
echo "Changes wiped."
|
||||
else
|
||||
echo "Operation aborted."
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env fish
|
||||
# sonr-consolidate.fish
|
||||
# Migrates all repos from orgs matching "sonr" into sonr-io,
|
||||
# prompting via gum for renames on collision, then deletes emptied orgs.
|
||||
#
|
||||
# Requires: gh (authenticated as org owner on all orgs), gum, jq
|
||||
|
||||
set -g TARGET_ORG sonr-io
|
||||
|
||||
# ── Preflight ────────────────────────────────────────────────────────────────
|
||||
for bin in gh gum jq
|
||||
if not command -q $bin
|
||||
echo (set_color red)"missing dependency: $bin"(set_color normal)
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
if not gh auth status &>/dev/null
|
||||
echo (set_color red)"gh is not authenticated"(set_color normal)
|
||||
exit 1
|
||||
end
|
||||
|
||||
# ── Discover source orgs ─────────────────────────────────────────────────────
|
||||
# All orgs the user belongs to whose login contains "sonr", excluding target.
|
||||
set -l source_orgs (gh api user/orgs --paginate --jq '.[].login' \
|
||||
| string match -r '.*sonr.*' \
|
||||
| string match -v $TARGET_ORG)
|
||||
|
||||
if test (count $source_orgs) -eq 0
|
||||
echo "No source orgs found. Nothing to do."
|
||||
exit 0
|
||||
end
|
||||
|
||||
echo (set_color cyan)"Source orgs:"(set_color normal)
|
||||
printf ' %s\n' $source_orgs
|
||||
echo (set_color cyan)"Target org:"(set_color normal)" $TARGET_ORG"
|
||||
echo
|
||||
|
||||
# Let the user confirm / trim the list interactively
|
||||
set -l selected_orgs (printf '%s\n' $source_orgs | gum choose --no-limit \
|
||||
--header "Select orgs to migrate into $TARGET_ORG (space to toggle, enter to confirm)")
|
||||
|
||||
if test (count $selected_orgs) -eq 0
|
||||
echo "Nothing selected. Exiting."
|
||||
exit 0
|
||||
end
|
||||
|
||||
# ── Helpers ──────────────────────────────────────────────────────────────────
|
||||
function repo_exists_in_target
|
||||
gh api "repos/$TARGET_ORG/$argv[1]" --silent &>/dev/null
|
||||
end
|
||||
|
||||
function transfer_repo
|
||||
# $1 = source org, $2 = repo name, $3 = (optional) new name
|
||||
set -l src $argv[1]
|
||||
set -l repo $argv[2]
|
||||
set -l args -f new_owner=$TARGET_ORG
|
||||
if set -q argv[3]; and test -n "$argv[3]"
|
||||
set -a args -f new_name=$argv[3]
|
||||
end
|
||||
gh api -X POST "repos/$src/$repo/transfer" $args --silent
|
||||
end
|
||||
|
||||
# ── Migration ────────────────────────────────────────────────────────────────
|
||||
set -g failed_transfers
|
||||
|
||||
for org in $selected_orgs
|
||||
echo
|
||||
echo (set_color --bold cyan)"── $org ──"(set_color normal)
|
||||
|
||||
set -l repos (gh api "orgs/$org/repos" --paginate --jq '.[].name')
|
||||
|
||||
if test (count $repos) -eq 0
|
||||
echo " (no repos)"
|
||||
continue
|
||||
end
|
||||
|
||||
for repo in $repos
|
||||
set -l new_name ""
|
||||
|
||||
if repo_exists_in_target $repo
|
||||
echo (set_color yellow)" ⚠ $repo already exists in $TARGET_ORG"(set_color normal)
|
||||
while true
|
||||
set new_name (gum input \
|
||||
--header "New name for $org/$repo (exists in $TARGET_ORG)" \
|
||||
--placeholder "$org-$repo" \
|
||||
--value "$org-$repo")
|
||||
if test -z "$new_name"
|
||||
echo " skipped $org/$repo"
|
||||
set new_name __SKIP__
|
||||
break
|
||||
end
|
||||
if repo_exists_in_target $new_name
|
||||
echo (set_color yellow)" $new_name also exists — pick another"(set_color normal)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
test "$new_name" = __SKIP__; and continue
|
||||
end
|
||||
|
||||
set -l label "$org/$repo → $TARGET_ORG/"(test -n "$new_name"; and echo $new_name; or echo $repo)
|
||||
if transfer_repo $org $repo $new_name
|
||||
echo (set_color green)" ✓ $label"(set_color normal)
|
||||
else
|
||||
echo (set_color red)" ✗ failed: $label"(set_color normal)
|
||||
set -a failed_transfers "$org/$repo"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if test (count $failed_transfers) -gt 0
|
||||
echo
|
||||
echo (set_color red)"Failed transfers (orgs containing these will NOT be deleted):"(set_color normal)
|
||||
printf ' %s\n' $failed_transfers
|
||||
end
|
||||
|
||||
# ── Wait for transfers to settle ─────────────────────────────────────────────
|
||||
# GitHub repo transfers are asynchronous; give them time to drain.
|
||||
echo
|
||||
gum spin --title "Waiting 30s for transfers to settle..." -- sleep 30
|
||||
|
||||
# ── Delete emptied orgs ──────────────────────────────────────────────────────
|
||||
echo
|
||||
for org in $selected_orgs
|
||||
# Skip orgs with failed transfers
|
||||
if string match -q "$org/*" -- $failed_transfers
|
||||
echo (set_color yellow)"skipping delete: $org (had failures)"(set_color normal)
|
||||
continue
|
||||
end
|
||||
|
||||
set -l remaining (gh api "orgs/$org/repos" --paginate --jq '. | length' | jq -s 'add // 0')
|
||||
|
||||
if test "$remaining" -gt 0
|
||||
echo (set_color yellow)"skipping delete: $org still has $remaining repo(s) (transfer pending?)"(set_color normal)
|
||||
continue
|
||||
end
|
||||
|
||||
if gum confirm "Delete empty org $org?"
|
||||
if gh api -X DELETE "orgs/$org" --silent
|
||||
echo (set_color green)"✓ deleted $org"(set_color normal)
|
||||
else
|
||||
echo (set_color red)"✗ could not delete $org (API delete requires org owner; may need manual deletion in Settings)"(set_color normal)
|
||||
end
|
||||
else
|
||||
echo "kept $org"
|
||||
end
|
||||
end
|
||||
|
||||
echo
|
||||
echo (set_color --bold green)"Done."(set_color normal)
|
||||
@@ -0,0 +1,22 @@
|
||||
function git-summary --description "Aggregate all AGENTS.md files in the current git repo via code2prompt"
|
||||
# Resolve the repo root so it works from any subdirectory
|
||||
set -l repo_root (git rev-parse --show-toplevel 2>/dev/null)
|
||||
if test -z "$repo_root"
|
||||
echo "git-summary: not inside a git repository" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
set -l template ~/.config/code2prompt/agents-md.hbs
|
||||
if not test -f $template
|
||||
echo "git-summary: template not found at $template" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
# Pass through any extra flags (e.g. --output-file, --tokens)
|
||||
code2prompt $repo_root \
|
||||
--include "AGENTS.md" \
|
||||
--include "**/AGENTS.md" \
|
||||
--template $template \
|
||||
$argv
|
||||
end
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# ~/.config/fish/functions/gtm.fish
|
||||
# Migrate GitHub repos → prad.codes (Gitea) via tea.
|
||||
# Usage: gtm [<owner>/<repo> | <github-url>] — no arg: multi-select via fzf
|
||||
|
||||
function gtm --description "Migrate GitHub repo(s) to a prad.codes org via tea"
|
||||
# -- Preflight ------------------------------------------------------------
|
||||
for bin in gh tea gum jq
|
||||
if not command -q $bin
|
||||
echo "gtm: missing dependency: $bin" >&2
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
set -l gh_token (gh auth token 2>/dev/null)
|
||||
if test -z "$gh_token"
|
||||
echo "gtm: gh not authenticated (run: gh auth login)" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
# -- Resolve source repos -------------------------------------------------
|
||||
# Each entry: nameWithOwner \t isPrivate
|
||||
set -l repos
|
||||
|
||||
if test (count $argv) -ge 1
|
||||
# Direct mode: accepts owner/repo, https://…, ssh://…, git@host:…
|
||||
# Same normalization as ghg-clone: strip scheme, user@, scp colon, .git
|
||||
set -l rel (
|
||||
printf '%s\n' $argv[1] \
|
||||
| sed -E 's#^[[:alnum:]]+://##; s#^[^@/]+@##; s#:#/#; s#\.git$##; s#/+$##'
|
||||
)
|
||||
# Reduce to owner/repo: take the last two path segments
|
||||
set -l parts (string split / -- $rel | string match -rv '^$')
|
||||
if test (count $parts) -lt 2
|
||||
echo "usage: gtm [<owner>/<repo> | <github-url>]" >&2
|
||||
return 1
|
||||
end
|
||||
# If a host segment is present, it must be github.com — that's the
|
||||
# only source this migrator pulls from (clone-url is hardcoded to it)
|
||||
if test (count $parts) -gt 2; and test "$parts[-3]" != github.com
|
||||
echo "gtm: source must be a github.com repo, got host: $parts[-3]" >&2
|
||||
return 1
|
||||
end
|
||||
set -l nwo "$parts[-2]/$parts[-1]"
|
||||
if not string match -qr '^[\w.-]+/[\w.-]+$' -- $nwo
|
||||
echo "gtm: could not parse owner/repo from: $argv[1]" >&2
|
||||
return 1
|
||||
end
|
||||
set -l meta (
|
||||
gh repo view $nwo \
|
||||
--json nameWithOwner,isPrivate \
|
||||
--jq '[.nameWithOwner, (.isPrivate|tostring)] | @tsv' 2>/dev/null
|
||||
)
|
||||
if test -z "$meta"
|
||||
echo "gtm: repo not found or inaccessible: $nwo" >&2
|
||||
return 1
|
||||
end
|
||||
set repos $meta
|
||||
else
|
||||
# Interactive mode: fzf multi-select
|
||||
if not command -q fzf
|
||||
echo "gtm: missing dependency: fzf" >&2
|
||||
return 1
|
||||
end
|
||||
set repos (
|
||||
gh repo list --limit 200 \
|
||||
--json nameWithOwner,isPrivate,description \
|
||||
--jq '.[] | [.nameWithOwner, (.isPrivate|tostring), .description // ""] | @tsv' \
|
||||
| fzf --multi \
|
||||
--delimiter '\t' \
|
||||
--with-nth 1,3 \
|
||||
--prompt 'migrate> ' \
|
||||
--header 'TAB: multi-select · ENTER: confirm' \
|
||||
--preview 'gh repo view {1}' \
|
||||
--preview-window 'right:50%:wrap'
|
||||
)
|
||||
if test (count $repos) -eq 0
|
||||
echo "gtm: nothing selected" >&2
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
# -- Select target org on prad.codes --------------------------------------
|
||||
set -l owner (
|
||||
tea organizations list -o simple \
|
||||
| string trim \
|
||||
| string match -rv '^$' \
|
||||
| gum choose --header "Target org on prad.codes"
|
||||
)
|
||||
if test -z "$owner"
|
||||
echo "gtm: no target org selected" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
# -- Options --------------------------------------------------------------
|
||||
set -l extras (
|
||||
gum choose --no-limit \
|
||||
--header "Migration options (space to toggle)" \
|
||||
"wiki" "issues" "labels" "pull-requests" "releases" "milestones" "lfs" "mirror"
|
||||
)
|
||||
set -l extra_flags
|
||||
for opt in $extras
|
||||
set -a extra_flags "--$opt"
|
||||
end
|
||||
|
||||
# -- Migrate --------------------------------------------------------------
|
||||
set -l failed
|
||||
set -l migrated # repo names successfully landed on prad.codes/$owner
|
||||
for line in $repos
|
||||
set -l fields (string split \t -- $line)
|
||||
set -l nwo $fields[1]
|
||||
set -l is_private $fields[2]
|
||||
set -l name (string split -m1 / -- $nwo)[2]
|
||||
|
||||
set -l flags --name $name --owner $owner \
|
||||
--clone-url "https://github.com/$nwo.git" \
|
||||
--service git \
|
||||
--auth-token $gh_token \
|
||||
$extra_flags
|
||||
|
||||
if test "$is_private" = true
|
||||
set -a flags --private
|
||||
end
|
||||
|
||||
echo "→ migrating $nwo → prad.codes/$owner/$name"
|
||||
if tea repos migrate $flags
|
||||
set -a migrated $name
|
||||
else
|
||||
set -a failed $nwo
|
||||
echo "✗ failed: $nwo" >&2
|
||||
end
|
||||
end
|
||||
|
||||
# -- Optional local clone -------------------------------------------------
|
||||
if test (count $migrated) -gt 0
|
||||
and gum confirm "Clone "(count $migrated)" migrated repo(s) locally via ghg-clone?"
|
||||
set -l last_dir
|
||||
for name in $migrated
|
||||
set -l dir (ghg-clone "https://prad.codes/$owner/$name")
|
||||
if test -n "$dir"
|
||||
set last_dir $dir
|
||||
else
|
||||
echo "✗ clone failed: $owner/$name" >&2
|
||||
end
|
||||
end
|
||||
# Single repo → drop into it (function runs in-shell, so cd sticks)
|
||||
if test (count $migrated) -eq 1 -a -n "$last_dir"
|
||||
cd $last_dir
|
||||
end
|
||||
end
|
||||
|
||||
# -- Summary --------------------------------------------------------------
|
||||
if test (count $failed) -gt 0
|
||||
echo
|
||||
echo "gtm: "(count $failed)" migration(s) failed:" >&2
|
||||
printf ' %s\n' $failed >&2
|
||||
return 1
|
||||
end
|
||||
echo
|
||||
echo "✓ done — "(count $repos)" repo(s) migrated to $owner"
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
function launch_omp_livediff_overlay --description "Overlay omp on the current kitty window with a livediff split beside it"
|
||||
# kitty appends -PID to config-file listen_on sockets, so a hardcoded
|
||||
# --to unix:@kitty-local never matches. kitty @ resolves the real
|
||||
# address from $KITTY_LISTEN_ON on its own.
|
||||
if not set -q KITTY_LISTEN_ON
|
||||
echo "launch_omp_livediff_overlay: no kitty remote-control socket (KITTY_LISTEN_ON unset)" >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
# omp overlays the current window; capture its window id
|
||||
set -l wid (kitty @ launch --type=overlay-main --cwd=current omp)
|
||||
or return
|
||||
|
||||
# livediff side by side in the same tab; keep focus on omp
|
||||
kitty @ launch --match id:$wid --location=vsplit --keep-focus --cwd=current livediff
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
function lc-elir -d "fzf-pick LeetCode solution files and run /leetcode-eli5 on each via omp"
|
||||
set -l root (count $argv > /dev/null; and echo $argv[1]; or echo .)
|
||||
# Match only numbered problem files: 1365.some-slug.js anywhere in the tree
|
||||
set -l files (fd --type f '^\d+\..+\.(js|ts)$' $root \
|
||||
| fzf --multi \
|
||||
--prompt='eli5> ' \
|
||||
--delimiter=/ \
|
||||
--with-nth=-3.. \
|
||||
--preview 'bat --color=always --line-range=:40 {}' \
|
||||
--preview-window=right:60%)
|
||||
test -z "$files"; and return 1
|
||||
for f in $files
|
||||
echo "── /leetcode-eli5 $f"
|
||||
omp -p "/leetcode-eli5 $f"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
function mdcp -a url
|
||||
if test -z "$url"
|
||||
echo "usage: mdcp <url>" >&2
|
||||
return 1
|
||||
end
|
||||
curl.md $url | cpy
|
||||
pst | mdfried
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
# ~/.config/fish/functions/mdv.fish
|
||||
function mdp -d "Interactive Markdown selector and previewer"
|
||||
# Find markdown files up to 3 directories deep
|
||||
set -l file (fd --type f --extension md --max-depth 3 | fzf --preview 'mdfried {}' --preview-window=right:60%:wrap)
|
||||
|
||||
# Execute mdfried on the selected file if one was chosen
|
||||
if test -n "$file"
|
||||
mdfried "$file"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,86 @@
|
||||
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 @@
|
||||
function y
|
||||
set tmp (mktemp -t "yazi-cwd.XXXXXX")
|
||||
command yazi $argv --cwd-file="$tmp"
|
||||
if read -z cwd < "$tmp"; and [ "$cwd" != "$PWD" ]; and test -d "$cwd"
|
||||
builtin cd -- "$cwd"
|
||||
end
|
||||
command rm -f -- "$tmp"
|
||||
end
|
||||
@@ -5,7 +5,7 @@
|
||||
# prefix byte is the shift variant (\x02N == prefix+shift+n).
|
||||
|
||||
# ── base / appearance ──
|
||||
font_size 12.5
|
||||
font_size 11.5
|
||||
font_family VictorMono Nerd Font
|
||||
bold_italic_font auto
|
||||
window_padding_width 8
|
||||
@@ -19,6 +19,7 @@ cursor_trail 10
|
||||
cursor_trail_start_threshold 0
|
||||
cursor_trail_decay 0.01 0.05
|
||||
cursor_blink_interval 0
|
||||
class kitty-herdr
|
||||
|
||||
# herdr renders its own sidebar/tab strip — no kitty chrome
|
||||
tab_bar_style hidden
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ── base.conf · shared ──
|
||||
font_size 12.5
|
||||
font_size 13.5
|
||||
font_family VictorMono Nerd Font
|
||||
bold_italic_font auto
|
||||
window_padding_width 8
|
||||
@@ -49,9 +49,9 @@ map ctrl+shift+f toggle_layout stack
|
||||
map super+z toggle_layout stack
|
||||
map ctrl+shift+e launch_tb nvim
|
||||
# herdr host: a *separate* kitty process, own config (launch cannot take --config)
|
||||
map ctrl+shift+a launch --type=background --cwd=current kitty --config ~/.config/kitty/herdr.conf
|
||||
map ctrl+shift+a launch --type=background --cwd=current kitty --class=kitty-herdr --config ~/.config/kitty/herdr.conf
|
||||
# tmux host: same trick — kitty stays dumb, tmux owns panes/windows/sessions
|
||||
map ctrl+shift+t launch --type=background --cwd=current kitty --config ~/.config/kitty/tmux.conf
|
||||
map ctrl+shift+t launch --type=background --cwd=current kitty --class=kitty-tmux --config ~/.config/kitty/tmux.conf
|
||||
map ctrl+shift+p launch_hs atuin-pick
|
||||
|
||||
# Misc
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Launched from the default config with ctrl+shift+t.
|
||||
|
||||
# ── base / appearance (kitty.conf parity) ──
|
||||
font_size 12.5
|
||||
font_size 11.5
|
||||
font_family VictorMono Nerd Font
|
||||
bold_italic_font auto
|
||||
window_padding_width 8
|
||||
|
||||
@@ -91,6 +91,14 @@ window-rule {
|
||||
// open-on-output "HDMI-A-1"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="kitty"
|
||||
match app-id="zed"
|
||||
match app-id="obsidian"
|
||||
|
||||
open-on-output "Apple Computer Inc StudioDisplay 0x984262A7"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="sticky-notest-canvas"
|
||||
match title="Sticky Notes"
|
||||
@@ -103,14 +111,9 @@ window-rule {
|
||||
match app-id="com.anthropic.Claude"
|
||||
match app-id="Hermes"
|
||||
match app-id="chromium"
|
||||
match app-id="kitty-herdr"
|
||||
match app-id="kitty-tmux"
|
||||
|
||||
open-on-output "eDP-1"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="kitty"
|
||||
match app-id="zed"
|
||||
match app-id="obsidian"
|
||||
|
||||
open-on-output "Apple Computer Inc StudioDisplay 0x984262A7"
|
||||
}
|
||||
@@ -25,8 +25,8 @@ hash = "7de446280b1ba8c49ffd86dbc24bbbe6"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "mikavilpas/easyjump.yazi:easyjump"
|
||||
rev = "a2a7373"
|
||||
hash = "6f8b9e41dde4ae29d958d4cdc829c121"
|
||||
rev = "da7e2d1"
|
||||
hash = "49625b69287597c3b8093c9f1a2f7241"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "cap153/tv"
|
||||
|
||||
@@ -4,4 +4,4 @@ symbolPreset: nerd
|
||||
setupVersion: 1
|
||||
hideThinkingBlock: true
|
||||
modelRoles:
|
||||
default: opencode-zen/claude-opus-5
|
||||
default: anthropic/claude-fable-5:high
|
||||
Reference in new issue
Block a user