diff --git a/dot_agents/dot_skill-lock.json b/dot_agents/dot_skill-lock.json index 8bec60b..2c571dc 100644 --- a/dot_agents/dot_skill-lock.json +++ b/dot_agents/dot_skill-lock.json @@ -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", diff --git a/dot_agents/skills/react/SKILL.md b/dot_agents/skills/react/SKILL.md index f78a08f..3f71719 100644 --- a/dot_agents/skills/react/SKILL.md +++ b/dot_agents/skills/react/SKILL.md @@ -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": , "$then": , "$else": }`** - conditional value - **`{ "$template": "Hello, ${/name}!" }`** - interpolates state values into strings - **`{ "$computed": "fn", "args": { ... } }`** - calls registered functions with resolved args diff --git a/dot_agents/skills/remix/references/assets-and-browser-modules.md b/dot_agents/skills/remix/references/assets-and-browser-modules.md index 8178309..046a134 100644 --- a/dot_agents/skills/remix/references/assets-and-browser-modules.md +++ b/dot_agents/skills/remix/references/assets-and-browser-modules.md @@ -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 diff --git a/dot_agents/skills/remix/references/hydration-frames-navigation.md b/dot_agents/skills/remix/references/hydration-frames-navigation.md index f438f33..1c47da2 100644 --- a/dot_agents/skills/remix/references/hydration-frames-navigation.md +++ b/dot_agents/skills/remix/references/hydration-frames-navigation.md @@ -76,9 +76,15 @@ let stream = renderToStream(, { 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 `` elements. Functions and class instances cannot be passed. +The resolved `preloads` array contains browser module hrefs. During server rendering these are emitted as `` 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: diff --git a/dot_config/fish/conf.d/20-aliases.fish.tmpl b/dot_config/fish/conf.d/20-aliases.fish similarity index 53% rename from dot_config/fish/conf.d/20-aliases.fish.tmpl rename to dot_config/fish/conf.d/20-aliases.fish index 4fb47ec..a0fffff 100644 --- a/dot_config/fish/conf.d/20-aliases.fish.tmpl +++ b/dot_config/fish/conf.d/20-aliases.fish @@ -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 diff --git a/dot_config/fish/conf.d/atuin.env.fish b/dot_config/fish/conf.d/atuin.env.fish new file mode 100644 index 0000000..d058327 --- /dev/null +++ b/dot_config/fish/conf.d/atuin.env.fish @@ -0,0 +1,2 @@ + +source "$HOME/.atuin/bin/env.fish" diff --git a/dot_config/fish/conf.d/opencode-background-subagents.fish b/dot_config/fish/conf.d/opencode-background-subagents.fish new file mode 100644 index 0000000..348257d --- /dev/null +++ b/dot_config/fish/conf.d/opencode-background-subagents.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 <<< diff --git a/dot_config/fish/config.fish.bak.1783797282 b/dot_config/fish/config.fish.bak.1783797282 new file mode 100644 index 0000000..92cdbcd --- /dev/null +++ b/dot_config/fish/config.fish.bak.1783797282 @@ -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 diff --git a/dot_config/fish/functions/ai.fish b/dot_config/fish/functions/ai.fish new file mode 100644 index 0000000..dff55e5 --- /dev/null +++ b/dot_config/fish/functions/ai.fish @@ -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 diff --git a/dot_config/fish/functions/c2p.fish b/dot_config/fish/functions/c2p.fish new file mode 100644 index 0000000..106a3c6 --- /dev/null +++ b/dot_config/fish/functions/c2p.fish @@ -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 diff --git a/dot_config/fish/functions/cp-appid.fish b/dot_config/fish/functions/cp-appid.fish new file mode 100644 index 0000000..6489665 --- /dev/null +++ b/dot_config/fish/functions/cp-appid.fish @@ -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 diff --git a/dot_config/fish/functions/czs.fish b/dot_config/fish/functions/czs.fish new file mode 100644 index 0000000..23d0ce1 --- /dev/null +++ b/dot_config/fish/functions/czs.fish @@ -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 diff --git a/dot_config/fish/functions/extract_agents.fish b/dot_config/fish/functions/extract_agents.fish new file mode 100644 index 0000000..5c048a2 --- /dev/null +++ b/dot_config/fish/functions/extract_agents.fish @@ -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 " + 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 diff --git a/dot_config/fish/functions/fab.fish b/dot_config/fish/functions/fab.fish new file mode 100644 index 0000000..f83dec6 --- /dev/null +++ b/dot_config/fish/functions/fab.fish @@ -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 diff --git a/dot_config/fish/functions/gdc.fish b/dot_config/fish/functions/gdc.fish new file mode 100644 index 0000000..7707748 --- /dev/null +++ b/dot_config/fish/functions/gdc.fish @@ -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 diff --git a/dot_config/fish/functions/gh-sonr-org-migrate.fish b/dot_config/fish/functions/gh-sonr-org-migrate.fish new file mode 100644 index 0000000..e960b7a --- /dev/null +++ b/dot_config/fish/functions/gh-sonr-org-migrate.fish @@ -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) diff --git a/dot_config/fish/functions/git-summary.fish b/dot_config/fish/functions/git-summary.fish new file mode 100644 index 0000000..349cabf --- /dev/null +++ b/dot_config/fish/functions/git-summary.fish @@ -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 + diff --git a/dot_config/fish/functions/gtm.fish b/dot_config/fish/functions/gtm.fish new file mode 100644 index 0000000..4002f12 --- /dev/null +++ b/dot_config/fish/functions/gtm.fish @@ -0,0 +1,160 @@ +# ~/.config/fish/functions/gtm.fish +# Migrate GitHub repos → prad.codes (Gitea) via tea. +# Usage: gtm [/ | ] — 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 [/ | ]" >&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 diff --git a/dot_config/fish/functions/launch_omp_livediff_overlay.fish b/dot_config/fish/functions/launch_omp_livediff_overlay.fish new file mode 100644 index 0000000..484f65d --- /dev/null +++ b/dot_config/fish/functions/launch_omp_livediff_overlay.fish @@ -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 diff --git a/dot_config/fish/functions/lc-elir.fish b/dot_config/fish/functions/lc-elir.fish new file mode 100644 index 0000000..a085a31 --- /dev/null +++ b/dot_config/fish/functions/lc-elir.fish @@ -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 diff --git a/dot_config/fish/functions/mdcp.fish b/dot_config/fish/functions/mdcp.fish new file mode 100644 index 0000000..908da8a --- /dev/null +++ b/dot_config/fish/functions/mdcp.fish @@ -0,0 +1,8 @@ +function mdcp -a url + if test -z "$url" + echo "usage: mdcp " >&2 + return 1 + end + curl.md $url | cpy + pst | mdfried +end diff --git a/dot_config/fish/functions/mdp.fish b/dot_config/fish/functions/mdp.fish new file mode 100644 index 0000000..556ba1c --- /dev/null +++ b/dot_config/fish/functions/mdp.fish @@ -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 diff --git a/dot_config/fish/functions/pj.fish b/dot_config/fish/functions/pj.fish new file mode 100644 index 0000000..d7a799e --- /dev/null +++ b/dot_config/fish/functions/pj.fish @@ -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 diff --git a/dot_config/fish/functions/y.fish b/dot_config/fish/functions/y.fish new file mode 100644 index 0000000..80ef371 --- /dev/null +++ b/dot_config/fish/functions/y.fish @@ -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 diff --git a/dot_config/kitty/herdr.conf b/dot_config/kitty/herdr.conf index d2cfabe..03b86b6 100644 --- a/dot_config/kitty/herdr.conf +++ b/dot_config/kitty/herdr.conf @@ -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 diff --git a/dot_config/kitty/kitty.conf b/dot_config/kitty/kitty.conf index 9173923..4516dd9 100644 --- a/dot_config/kitty/kitty.conf +++ b/dot_config/kitty/kitty.conf @@ -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 diff --git a/dot_config/kitty/tmux.conf b/dot_config/kitty/tmux.conf index b9bf2d3..c241c86 100644 --- a/dot_config/kitty/tmux.conf +++ b/dot_config/kitty/tmux.conf @@ -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 diff --git a/dot_config/niri/exact_config.d/30-window-rules.kdl b/dot_config/niri/exact_config.d/30-window-rules.kdl index c633b07..84cef81 100644 --- a/dot_config/niri/exact_config.d/30-window-rules.kdl +++ b/dot_config/niri/exact_config.d/30-window-rules.kdl @@ -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" -} diff --git a/dot_config/yazi/package.toml b/dot_config/yazi/package.toml index f54d19b..3707872 100644 --- a/dot_config/yazi/package.toml +++ b/dot_config/yazi/package.toml @@ -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" diff --git a/dot_omp/private_agent/private_config.yml b/dot_omp/private_agent/private_config.yml index aed65d9..af08b32 100644 --- a/dot_omp/private_agent/private_config.yml +++ b/dot_omp/private_agent/private_config.yml @@ -4,4 +4,4 @@ symbolPreset: nerd setupVersion: 1 hideThinkingBlock: true modelRoles: - default: opencode-zen/claude-opus-5 \ No newline at end of file + default: anthropic/claude-fable-5:high \ No newline at end of file