39 Commits

Author SHA1 Message Date
David Tomaschik
9770514d6a Finish brew updates 2026-05-25 15:11:06 -07:00
David Tomaschik
e1724c77f3 Update Brewfile 2026-05-22 18:23:18 -07:00
David Tomaschik
ecbc25e5ac Improve skelify 2026-05-20 14:35:14 -07:00
David Tomaschik
ea33840ef6 Merge branch 'main' of https://github.com/Matir/skel 2026-05-19 17:02:08 -07:00
David Tomaschik
1048775da3 Update skel 2026-05-19 17:02:05 -07:00
David Tomaschik
c9af80bc5d Merge branch 'main' of https://github.com/Matir/skel 2026-05-19 13:44:52 -07:00
David Tomaschik
d8b4991419 End homebrew hints 2026-05-19 13:44:38 -07:00
David Tomaschik
158d9f6e4e Update Brewfile 2026-05-09 18:47:15 -07:00
David Tomaschik
b1d1c42a02 bump brewfile 2026-05-07 13:43:44 -07:00
David Tomaschik
f8ec9cc338 fix update_brewfile 2026-05-07 08:56:32 -07:00
David Tomaschik
6e3c3dd269 Move keybase to off-corp brewfile 2026-05-07 08:50:45 -07:00
David Tomaschik
77b8374871 Add difft difftool 2026-05-07 08:41:59 -07:00
David Tomaschik
4645682b5c Merge branch 'main' of github.com:Matir/skel 2026-05-07 01:16:56 -07:00
David Tomaschik
75bdebb497 bump 2026-05-07 01:16:54 -07:00
David Tomaschik
4e72b9b18c Update gitconfig 2026-04-28 15:20:24 -07:00
David Tomaschik
bd2c2287cd Add more functions 2026-04-23 10:34:21 -07:00
David Tomaschik
db2c02bd2d Cleanup 2026-04-21 15:59:38 -07:00
David Tomaschik
fec16225e4 Build update-authorized-keys 2026-04-21 14:39:32 -07:00
David Tomaschik
fa6a878487 Fix SSH agent forwarding clobbered by local agent in shenv (#14)
* Fix SSH agent forwarding clobbered by local agent in shenv

ssh/rc saves the raw forwarded socket in SSH_REMOTE_AUTH_SOCK before
rewriting SSH_AUTH_SOCK to the stable symlink. shenv was ignoring that
variable, so it saw SSH_AUTH_SOCK as "our link" and fell through to the
systemd lookup, which could overwrite the symlink with a local agent
socket and silently drop the forwarded one.

Now shenv checks SSH_REMOTE_AUTH_SOCK first, giving forwarded sockets
priority over any local agent.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA

* Fix shenv clobbering forwarded SSH socket with local agent in tmux

ssh/rc env changes (including SSH_REMOTE_AUTH_SOCK) are lost because
ssh/rc runs as a sshd child process, not the user's shell. The shell
always receives SSH_AUTH_SOCK set to the raw forwarded socket path.

Fresh SSH login worked fine (step 1 catches the raw socket). The bug
was in tmux new windows: SSH_AUTH_SOCK there is our stable symlink, so
step 1 fails, then steps 2/3 look up the system agent and overwrite the
symlink that ssh/rc just set to the forwarded socket.

Fix: only run the system agent lookup when the stable symlink is already
broken. A valid symlink means ssh/rc (or a previous shenv run) already
set it correctly; don't clobber it.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA

* Remove pointless exports from ssh/rc, add process-model comment

ssh/rc runs as a sshd child process so exports never reach the user's
shell. SSH_REMOTE_AUTH_SOCK was set and exported but never used (a
leftover from a prior failed fix attempt). SSH_AUTH_SOCK was reassigned
to the symlink path and exported, also to no effect. Remove both.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 19:20:43 -07:00
David Tomaschik
1804357162 Update skel 2026-04-14 10:27:17 -07:00
David Tomaschik
202d871a59 Merge branch 'main' of github.com:Matir/skel 2026-04-09 21:18:29 -07:00
David Tomaschik
467d916f33 Update zshrc 2026-04-09 21:18:24 -07:00
David Tomaschik
6d2bfdbcea Update custom starship shell 2026-04-09 18:03:14 -07:00
David Tomaschik
1d0a09c442 Bump Brewfile 2026-04-07 16:32:02 -07:00
David Tomaschik
37c765ae29 Update for bundles 2026-04-07 16:02:49 -07:00
David Tomaschik
41f8a49381 Remove missing brew entry 2026-04-07 14:53:41 -07:00
David Tomaschik
3f9c41d266 Merge branch 'main' of https://github.com/Matir/skel 2026-04-06 18:34:17 -07:00
David Tomaschik
69e4b83652 Fix brew manager 2026-04-06 18:34:06 -07:00
David Tomaschik
74c2472dd2 Fix zsh completion 2026-04-06 15:30:14 -07:00
David Tomaschik
ecc39344ca Improve gemini context 2026-04-02 16:37:51 -07:00
David Tomaschik
49a314e388 Merge branch 'main' of github.com:Matir/skel 2026-04-01 23:02:57 -07:00
David Tomaschik
41293eb788 Add more vscode extensions 2026-04-01 23:02:55 -07:00
David Tomaschik
05798dcb67 Update AGENTS.md 2026-04-01 18:24:15 -07:00
David Tomaschik
4e1b263170 Remove weechat 2026-04-01 14:23:54 -07:00
David Tomaschik
46c00c61be Update dotfiles 2026-04-01 14:02:33 -07:00
David Tomaschik
ce973d5bbf Update shenv SSH_AUTH_SOCK code 2026-03-31 14:06:57 -07:00
David Tomaschik
f326992306 Merge branch 'main' of https://github.com/Matir/skel 2026-03-31 13:41:38 -07:00
David Tomaschik
7f76b24cb9 Update SSH_AUTH_SOCK logic 2026-03-31 13:41:36 -07:00
David Tomaschik
a5b08680c5 Fix brew install issues 2026-03-31 13:25:52 -07:00
60 changed files with 1173 additions and 1490 deletions

View File

@@ -9,8 +9,9 @@ fi
UPDATE_SCRIPT="bin/macos/update_brewfile"
if [[ -x "$UPDATE_SCRIPT" ]]; then
# Run in dry-run mode and see if there's output
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run 2>/dev/null)
echo "🔍 Checking Brewfile synchronization..."
# Run in dry-run mode with --add-only and see if there's output
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run --add-only 2>/dev/null)
if [[ "$DIFF_OUTPUT" == *"Changes detected"* ]]; then
echo "⚠️ Brewfile is out of sync with your installed packages."
echo " Run '$UPDATE_SCRIPT' to synchronize it."

View File

@@ -10,7 +10,8 @@ locations, but also installs dependencies in various ways.
I primarily target Debian Linux-based (Debian, Ubuntu, and Kali Linux) systems
as well as MacOS. Other platforms are lower priorities. Shell scripts ending
in `.sh` should use only POSIX features unless there is a shebang line at the
beginning suggesting a different shell will be used.
beginning suggesting a different shell will be used. In particular, those
in directories with names like bash might use those shells.
`zsh` and `fish` are the key interactive shells to be configured, but `bash`
may also be used at times.

View File

@@ -1,5 +1,7 @@
tap "dart-lang/dart"
tap "holtwick/tap"
tap "sass/sass"
brew "ack"
brew "acme.sh"
brew "age"
@@ -7,18 +9,23 @@ brew "autoconf"
brew "automake"
brew "b2-tools"
brew "bat"
brew "binwalk"
brew "cask"
brew "ccache"
brew "certbot"
brew "cloudflared"
brew "cmake"
brew "colima"
brew "devcontainer"
brew "difftastic"
brew "dfu-util"
brew "difftastic"
brew "direnv"
brew "duck"
brew "dust"
brew "earthly"
brew "esptool"
brew "fish"
brew "fq"
brew "gh"
brew "ghidra", link: false
brew "git"
@@ -27,33 +34,41 @@ brew "git-lfs"
brew "gnupg"
brew "go"
brew "gradle"
brew "hf"
brew "holtwick/tap/bx"
brew "htop"
brew "httpie"
brew "huggingface-cli"
brew "hugo"
brew "imagemagick"
brew "john-jumbo"
brew "jq"
brew "kubeconform"
brew "kubectx"
brew "librsvg"
brew "lima"
brew "minikube"
brew "mise"
brew "mosh"
brew "neovim"
brew "ninja"
brew "nmap"
brew "protobuf"
brew "ollama"
brew "p7zip"
brew "pipenv"
brew "pipx"
brew "pkgconf"
brew "protobuf"
brew "pwgen"
brew "pwntools"
brew "python@3.13"
brew "qemu"
brew "restic"
brew "ripgrep"
brew "ruby"
brew "ruby@3.3"
brew "rustup"
brew "scroll-reverser"
brew "sass/sass/migrator"
brew "sass/sass/sass"
brew "shellcheck"
brew "smartmontools"
brew "starship"
@@ -64,8 +79,7 @@ brew "wget"
brew "yt-dlp"
brew "zlib"
brew "zsh-syntax-highlighting"
brew "sass/sass/migrator"
brew "sass/sass/sass"
cask "codeql"
cask "cyberduck"
cask "font-fira-code-nerd-font"
@@ -82,11 +96,13 @@ cask "iterm2"
cask "macfuse"
cask "meld"
cask "mitmproxy"
cask "processmonitor"
cask "raycast"
cask "rectangle"
cask "scroll-reverser"
cask "temurin"
cask "veracrypt"
cask "wezterm"
cask "zulu@17"
def is_corp?
@@ -94,13 +110,22 @@ def is_corp?
`profiles status -type enrollment 2>/dev/null`.include?("Enrolled via DEP: Yes")
end
if is_corp?
brew "bazelisk", link: false
end
# non-corp
if !is_corp?
brew "bazel"
brew "bazelisk"
brew "openssh"
brew "virt-manager"
cask "claude-code"
cask "cryptomator"
cask "keepassxc"
cask "gcloud-cli"
cask "google-cloud-sdk"
cask "keybase"
cask "orbstack"
cask "jordanbaird-ice"
end

View File

@@ -12,8 +12,17 @@ fi
trap "test -f ${FILENAME} && rm -f ${FILENAME}" EXIT
IOENGINE="libaio"
DIRECT=1
if [ "$(uname)" = "Darwin" ]; then
IOENGINE="posixaio"
# macOS doesn't support O_DIRECT in the same way, but fio's direct=1
# handles it via F_NOCACHE if supported.
DIRECT=1
fi
fio --loops=5 --size=${BENCHMARK_SIZE} --filename=${FILENAME} \
--stonewall --ioengine=libaio --direct=1 \
--stonewall --ioengine=${IOENGINE} --direct=${DIRECT} \
--name=Seqread --bs=1m --rw=read \
--name=Seqwrite --bs=1m --rw=write \
--name=512Kread --bs=512k --rw=randread \

View File

@@ -1,6 +1,10 @@
#!/bin/bash
CHROME_BINS="google-chrome-beta google-chrome"
if [ "$(uname)" = "Darwin" ]; then
CHROME_BINS="${CHROME_BINS} /Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
fi
for bin in ${CHROME_BINS} ; do
if command -v ${bin} >/dev/null 2>&1 ; then
CHROME=$(command -v ${bin})
@@ -18,4 +22,4 @@ export HOME=${HOME}/.chrome-pentest
mkdir -p ${HOME}
# Launch chrome for burp
exec ${CHROME} --user-data-dir=${HOME}/chrome-pentest --proxy-server=127.0.0.1:8080
exec "${CHROME}" --user-data-dir=${HOME}/chrome-pentest --proxy-server=127.0.0.1:8080

View File

@@ -7,16 +7,69 @@ import sys
import argparse
import difflib
import tempfile
# Regex to match brew/cask/tap/mas lines
PKG_RE = re.compile(r'^\s*(brew|cask|tap|mas)\s+["\']([^"\']+)["\'](.*)$')
def colorize_diff(lines):
for line in lines:
if line.startswith('+') and not line.startswith('+++'):
yield f"\033[32m{line}\033[0m"
elif line.startswith('-') and not line.startswith('---'):
yield f"\033[31m{line}\033[0m"
elif line.startswith('^'):
yield f"\033[36m{line}\033[0m"
else:
yield line
class Entry:
def sort_key(self): raise NotImplementedError()
def to_lines(self): raise NotImplementedError()
class PackageEntry(Entry):
def __init__(self, pkg_type, name, options, comments=None):
self.pkg_type = pkg_type
self.name = name
self.options = options.strip()
self.comments = comments or []
def sort_key(self):
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
return (order.get(self.pkg_type, 4), self.name)
def to_lines(self):
res = list(self.comments)
pkg_line = f'{self.pkg_type} "{self.name}"'
if self.options:
if not self.options.startswith(','):
pkg_line += ' '
pkg_line += self.options
res.append(pkg_line)
return res
class TextEntry(Entry):
def __init__(self, lines, is_header=True):
self.lines = lines
self.is_header = is_header
def sort_key(self):
# Header is -1, Trailing is 5 (after all package types 0-4)
return (-1 if self.is_header else 5, "")
def to_lines(self):
return self.lines
def get_repo_root():
script_dir = os.path.dirname(os.path.realpath(__file__))
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'],
cwd=script_dir,
stderr=subprocess.STDOUT).decode().strip()
return root
except subprocess.CalledProcessError:
return os.getcwd()
# If not in a git repo, go up 2 levels from script_dir (bin/macos/)
return os.path.abspath(os.path.join(script_dir, '..', '..'))
def get_ignore_list(repo_root):
ignore = set()
@@ -34,11 +87,19 @@ def get_ignore_list(repo_root):
ignore.add(line)
return ignore
def get_current_packages():
def get_current_packages(args):
"""Runs brew bundle dump and returns lines."""
env = os.environ.copy()
env["HOMEBREW_NO_AUTO_UPDATE"] = "1"
env["HOMEBREW_NO_INSTALL_CLEANUP"] = "1"
env["HOMEBREW_NO_ENV_HINTS"] = "1"
cmd = ['brew', 'bundle', 'dump', '--file=-']
if args.no_mas: cmd.append('--no-mas')
if args.no_vscode: cmd.append('--no-vscode')
try:
output = subprocess.check_output(['brew', 'bundle', 'dump', '--file=-'],
stderr=subprocess.DEVNULL).decode()
output = subprocess.check_output(cmd, env=env, stderr=subprocess.DEVNULL).decode()
return output.splitlines()
except subprocess.CalledProcessError:
print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr)
@@ -48,8 +109,9 @@ def parse_brewfile(content):
"""
Parses Brewfile content.
Returns:
- entries: list of Entry objects
- conditional_pkgs: set of (type, name)
- preserved_footer: string (everything from first conditional onwards)
- footer: string (everything from first conditional onwards)
"""
lines = content.splitlines()
conditional_pkgs = set()
@@ -60,7 +122,7 @@ def parse_brewfile(content):
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith(('if ', 'unless ', 'case ')) and not stripped.endswith('; end'):
if stripped.startswith(('if ', 'unless ', 'case ', 'def ', 'begin ')) and not stripped.endswith('; end'):
if first_conditional_idx == -1:
# Look back for comments that might belong to this block
j = i - 1
@@ -77,13 +139,35 @@ def parse_brewfile(content):
if stripped == 'end' or stripped.endswith('; end'):
in_conditional -= 1
if first_conditional_idx == -1:
return set(), ""
unconditional_lines = lines[:first_conditional_idx] if first_conditional_idx != -1 else lines
footer = "\n".join(lines[first_conditional_idx:]) if first_conditional_idx != -1 else ""
footer = "\n".join(lines[first_conditional_idx:])
return conditional_pkgs, footer
entries = []
comment_buffer = []
first_pkg_seen = False
for line in unconditional_lines:
match = PKG_RE.match(line)
if match:
if not first_pkg_seen:
if comment_buffer:
entries.append(TextEntry(comment_buffer, is_header=True))
comment_buffer = []
first_pkg_seen = True
entries.append(PackageEntry(match.group(1), match.group(2), match.group(3), comment_buffer))
comment_buffer = []
else:
comment_buffer.append(line)
if comment_buffer:
entries.append(TextEntry(comment_buffer, is_header=False))
return entries, conditional_pkgs, footer
def main(args):
if sys.platform != "darwin":
print(f"Warning: Running on {sys.platform}. Brewfile is primarily for macOS.", file=sys.stderr)
repo_root = get_repo_root()
brewfile_path = os.path.join(repo_root, 'Brewfile')
@@ -94,39 +178,90 @@ def main(args):
with open(brewfile_path) as f:
old_content = f.read()
conditional_pkgs, footer = parse_brewfile(old_content)
old_entries, conditional_pkgs, footer = parse_brewfile(old_content)
ignore_list = get_ignore_list(repo_root)
dumped_lines = get_current_packages()
new_unconditional_lines = []
old_pkg_map = {}
for e in old_entries:
if isinstance(e, PackageEntry):
key = (e.pkg_type, e.name)
if key not in old_pkg_map:
old_pkg_map[key] = e
dumped_lines = get_current_packages(args)
dumped_pkgs = []
for line in dumped_lines:
match = PKG_RE.match(line)
if match:
pkg_type, pkg_name = match.group(1), match.group(2)
if pkg_name in ignore_list:
continue
if (pkg_type, pkg_name) in conditional_pkgs:
pkg_type, pkg_name, pkg_options = match.group(1), match.group(2), match.group(3)
if pkg_name in ignore_list or (pkg_type, pkg_name) in conditional_pkgs:
continue
dumped_pkgs.append(PackageEntry(pkg_type, pkg_name, pkg_options))
# If it's not a package line (e.g. comment from dump), we can skip or keep
if line.strip():
new_unconditional_lines.append(line)
new_entries = []
for e in old_entries:
if isinstance(e, TextEntry) and e.is_header:
new_entries.append(e)
# Sort lines by type (tap, brew, cask, mas) then name
def sort_key(line):
match = PKG_RE.match(line)
if not match: return (4, line)
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
return (order.get(match.group(1), 4), match.group(2))
seen_in_new = set()
added_count = 0
removed_count = 0
merged_count = 0
new_unconditional_lines.sort(key=sort_key)
if args.add_only:
for e in old_entries:
if isinstance(e, PackageEntry):
new_entries.append(e)
seen_in_new.add((e.pkg_type, e.name))
for d in dumped_pkgs:
if (d.pkg_type, d.name) not in seen_in_new:
new_entries.append(d)
seen_in_new.add((d.pkg_type, d.name))
added_count += 1
else:
for d in dumped_pkgs:
key = (d.pkg_type, d.name)
if key in seen_in_new: continue
if key in old_pkg_map:
merged = old_pkg_map[key]
if not merged.options:
merged.options = d.options
new_entries.append(merged)
merged_count += 1
else:
new_entries.append(d)
added_count += 1
seen_in_new.add(key)
# Build new content
new_content = "\n".join(new_unconditional_lines)
# Check for removals
for key in old_pkg_map:
if key not in seen_in_new:
removed_count += 1
for e in old_entries:
if isinstance(e, TextEntry) and not e.is_header:
new_entries.append(e)
new_entries.sort(key=lambda x: x.sort_key())
output_lines = []
last_type = None
for e in new_entries:
if isinstance(e, PackageEntry):
if last_type and e.pkg_type != last_type:
output_lines.append("")
last_type = e.pkg_type
lines = e.to_lines()
# If we just added a blank line, and the new lines start with one, skip the first new line
if output_lines and output_lines[-1] == "" and lines and lines[0] == "":
output_lines.extend(lines[1:])
else:
output_lines.extend(lines)
new_content = "\n".join(output_lines)
if footer:
if new_unconditional_lines:
if output_lines and output_lines[-1].strip():
new_content += "\n\n"
new_content += footer.strip() + "\n"
else:
@@ -137,20 +272,33 @@ def main(args):
else:
if args.dry_run:
print("Changes detected (dry run):")
diff = difflib.unified_diff(
diff = list(difflib.unified_diff(
old_content.splitlines(keepends=True),
new_content.splitlines(keepends=True),
fromfile='Brewfile (original)',
tofile='Brewfile (new)'
)
))
if sys.stdout.isatty():
sys.stdout.writelines(colorize_diff(diff))
else:
sys.stdout.writelines(diff)
else:
with open(brewfile_path, 'w') as f:
f.write(new_content)
dir_name = os.path.dirname(brewfile_path)
with tempfile.NamedTemporaryFile('w', dir=dir_name, delete=False) as tf:
tf.write(new_content)
tempname = tf.name
os.replace(tempname, brewfile_path)
print("Brewfile updated.")
if args.verbose:
print(f"Summary: {added_count} added, {removed_count} removed, {merged_count} kept/merged.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update Brewfile while preserving conditionals.")
parser.add_argument("--dry-run", action="store_true", help="Show changes without applying them.")
parser.add_argument("--add-only", action="store_true", help="Only add missing entries, do not remove existing ones.")
parser.add_argument("--verbose", "-v", action="store_true", help="Print summary of changes.")
parser.add_argument("--no-mas", action="store_true", help="Do not include Mac App Store apps.")
parser.add_argument("--no-vscode", action="store_true", help="Do not include VSCode extensions.")
args = parser.parse_args()
main(args)

63
bin/quartz Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
set -euo pipefail
# QUARTZ_DIR search logic
if [ -z "${QUARTZ_DIR:-}" ]; then
if [ -f "quartz.config.ts" ]; then
QUARTZ_DIR="$PWD"
elif [ -d "$HOME/Personal/notes-quartz" ]; then
QUARTZ_DIR="$HOME/Personal/notes-quartz"
elif [ -d "$HOME/Projects/notes-quartz" ]; then
QUARTZ_DIR="$HOME/Projects/notes-quartz"
else
echo "Error: QUARTZ_DIR could not be found." >&2
exit 1
fi
fi
if [ ! -d "$QUARTZ_DIR" ]; then
echo "Error: QUARTZ_DIR '$QUARTZ_DIR' is not a directory." >&2
exit 1
fi
# NOTES_DIR search logic
PARSE_NOTES_DIR=""
# Use a copy of args to find -d/--directory
ARGS=("$@")
for ((i=0; i<${#ARGS[@]}; i++)); do
if [[ "${ARGS[i]}" == "-d" || "${ARGS[i]}" == "--directory" ]]; then
if [[ $((i+1)) -lt ${#ARGS[@]} ]]; then
PARSE_NOTES_DIR="${ARGS[i+1]}"
fi
break
fi
done
if [ -n "$PARSE_NOTES_DIR" ]; then
NOTES_DIR="$PARSE_NOTES_DIR"
elif [ -z "${NOTES_DIR:-}" ]; then
if [ -d "$HOME/Notes" ]; then
NOTES_DIR="$HOME/Notes"
elif [ -d "$HOME/Personal/notes" ]; then
NOTES_DIR="$HOME/Personal/notes"
elif [ -d "$HOME/Projects/notes" ]; then
NOTES_DIR="$HOME/Projects/notes"
else
echo "Error: NOTES_DIR could not be found." >&2
exit 1
fi
fi
if [ ! -d "$NOTES_DIR" ]; then
echo "Error: NOTES_DIR '$NOTES_DIR' is not a directory." >&2
exit 1
fi
cd "$QUARTZ_DIR"
# Run npx quartz
# Following the prompt's structure but using NOTES_DIR for the flag
# npx quartz ${argv[1]} --directory ${NOTES_DIR} "$@"
# We use ${1:-} for argv[1] to handle cases with no arguments.
npx quartz "${1:-}" --directory "$NOTES_DIR" "$@"

View File

@@ -5,8 +5,14 @@
set -ue
TOOLS="flameshot scrot"
if [ "$(uname)" = "Darwin" ]; then
TOOLS="screencapture ${TOOLS}"
fi
SCREENDIR=${SCREENDIR:-${HOME}/Pictures/Screenshots}
SCROT_FORMAT="%F-%T.png"
# Filename for screencapture
FILE_NAME=$(date "+%Y-%m-%d-%H%M%S.png")
function default_screenshot_command {
for tool in ${TOOLS} ; do
@@ -41,10 +47,29 @@ function scrot_full_capture {
scrot "${SCREENDIR}/${SCROT_FORMAT}"
}
function mac_capture {
local mode="${1}"
local target="${SCREENDIR}/${FILE_NAME}"
case "${mode}" in
region)
screencapture -i "${target}"
;;
window)
screencapture -i -w "${target}"
;;
full)
screencapture "${target}"
;;
esac
}
case "${CMD}" in
region|window|full)
mkdir -p "${SCREENDIR}"
case "${TOOL}" in
screencapture)
mac_capture "${CMD}"
;;
flameshot)
case "${CMD}" in
region|window)

310
bin/update-authorized-keys Executable file
View File

@@ -0,0 +1,310 @@
#!/usr/bin/env bash
# update-authorized-keys - Manage ~/.ssh/authorized_keys from multiple sources
#
# BEHAVIOR:
# 1. Collects SSH public keys from one or more source directories (default: ~/.ssh/authorized_keys.d).
# 2. Skips empty files and files symlinked to /dev/null (masking).
# 3. Deterministically concatenates keys into a "managed block" wrapped in markers:
# # BEGIN UPDATE-AUTHORIZED-KEYS
# # END UPDATE-AUTHORIZED-KEYS
# 4. Deduplicates managed keys: if the same key (including options) is found in multiple files,
# it is included once with a comment listing all source filenames.
# 5. Preserves "manual" keys found in the target file outside the markers.
# 6. Removes manual keys that exactly match a managed key (options + key data).
# 7. Validates every proposed key individually using 'ssh-keygen -l -f'.
# 8. Optionally validates the whole file with 'authorized-keys-test' if available.
# 9. Displays a unified diff and prompts for confirmation before atomic replacement.
# 10. Supports a --dry-run mode and a --self-test mode for verifying logic.
set -o nounset
set -o errexit
set -o pipefail
CLEANUP_FILES=()
cleanup() {
rm -rf "${CLEANUP_FILES[@]}"
}
trap cleanup EXIT
# Configuration
DEFAULT_DIR="${HOME}/.ssh/authorized_keys.d"
DEFAULT_TARGET="${HOME}/.ssh/authorized_keys"
BEGIN_MARKER="# BEGIN UPDATE-AUTHORIZED-KEYS"
END_MARKER="# END UPDATE-AUTHORIZED-KEYS"
# State
SOURCE_DIRS=()
TARGET_FILE="${DEFAULT_TARGET}"
DRY_RUN=0
usage() {
cat <<EOF
Usage: $(basename "$0") [options]
Options:
--dir DIR Primary directory for managed keys (default: ${DEFAULT_DIR})
--extra-dir DIR Additional directory to scan for keys (can be repeated)
--target FILE Target authorized_keys file (default: ${DEFAULT_TARGET})
--dry-run Show changes and validate without modifying the target
--self-test Run internal suite of tests to verify script logic
--help Show this help message
EOF
}
run_self_test() {
echo "Running self-test..."
local test_root=$(mktemp -d)
CLEANUP_FILES+=("${test_root}")
local d1="${test_root}/d1"
local d2="${test_root}/d2"
local target="${test_root}/target"
mkdir -p "${d1}" "${d2}"
local key1="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5X key1"
local key2="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL9YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6Y key2"
local key_man="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM0ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7Z manual"
local long_opt="environment=\"VAR=VERY_LONG_VALUE_THAT_EXCEEDS_TWENTY_CHARS\""
echo "${key1}" > "${d1}/k1"
echo "${key1}" > "${d2}/k1_dup"
echo "${key2}" > "${d2}/k2"
echo "${long_opt} ${key1}" > "${d1}/k1_long"
echo "${long_opt} ${key2}" > "${d1}/k2_long"
ln -s /dev/null "${d1}/masked"
cat <<EOF > "${target}"
${key_man}
${key1} # This should be removed as it's now managed
EOF
echo "Executing script in test mode..."
# Pipe "y" to handle the TTY check if we are not in a TTY during test
echo "y" | "$0" --dir "${d1}" --extra-dir "${d2}" --target "${target}" > /dev/null
local content=$(cat "${target}")
echo -n "Check markers... "
if [[ "${content}" == *"${BEGIN_MARKER}"* && "${content}" == *"${END_MARKER}"* ]]; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check managed deduplication... "
if grep -q "Source: k1, k1_dup" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check long option deduplication (should NOT deduplicate different keys)... "
if grep -q "k1_long" "${target}" && grep -q "k2_long" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check manual key preservation... "
if grep -q "manual" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check manual key filtering... "
local manual_count=$(grep -c "${key1}" "${target}")
# key1 appears twice in managed block (once plain, once with long opt)
# and it was in manual block. The manual one should be removed.
# So we expect 2 occurrences in the final file (both in managed block).
if [[ ${manual_count} -eq 2 ]]; then echo "OK"; else echo "FAIL (Found ${manual_count} occurrences, expected 2)"; exit 1; fi
echo -n "Check masking... "
if ! grep -q "masked" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo "Self-test passed successfully!"
exit 0
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--dir)
[[ -z "${2:-}" ]] && { echo "Error: --dir requires an argument" >&2; exit 1; }
SOURCE_DIRS+=("$2"); shift 2 ;;
--extra-dir)
[[ -z "${2:-}" ]] && { echo "Error: --extra-dir requires an argument" >&2; exit 1; }
SOURCE_DIRS+=("$2"); shift 2 ;;
--target)
[[ -z "${2:-}" ]] && { echo "Error: --target requires an argument" >&2; exit 1; }
TARGET_FILE="$2"; shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
--self-test) run_self_test ;;
--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done
if [[ ${#SOURCE_DIRS[@]} -eq 0 ]]; then
SOURCE_DIRS+=("${DEFAULT_DIR}")
fi
mkdir -p "$(dirname "${TARGET_FILE}")"
TMP_FILE=$(mktemp)
CLEANUP_FILES+=("${TMP_FILE}")
collect_keys() {
local dirs=("${@}")
for dir in "${dirs[@]}"; do
if [[ ! -d "${dir}" ]]; then continue; fi
# Use a glob to avoid parsing ls
for file in "${dir}"/*; do
[[ ! -e "${file}" ]] && continue
[[ ! -f "${file}" || ! -s "${file}" ]] && continue
if [[ -L "${file}" && "$(readlink "${file}")" == "/dev/null" ]]; then continue; fi
while read -r line; do
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
# Use a specific delimiter that is unlikely to be in the key or filename
# If using tabs, ensure we only split on the first one in AWK
printf "%s\t%s\n" "$(basename "${file}")" "${line}"
done < "${file}"
done
done
}
# Use a HEREDOC for the complex AWK script to avoid shell interpolation issues
MANAGED_BLOCK=$(collect_keys "${SOURCE_DIRS[@]}" | awk -F'\t' '
{
# Splitting on the first tab manually to be robust
tab_idx = index($0, "\t")
source = substr($0, 1, tab_idx - 1)
full_line = substr($0, tab_idx + 1)
# Signature detection: all options + key type + key data
# (Excludes the comment at the end)
n = split(full_line, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
# A key line is [options] <type> <base64> [comment]
# We stop after the base64 part. Key types start with known prefixes.
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
# Fallback if no key type found (should not happen with valid keys)
if (sig == "") sig = full_line
if (!(sig in keys)) {
keys[sig] = full_line
order[++count] = sig
}
sources[sig] = (sources[sig] ? sources[sig] ", " : "") source
}
END {
for (i=1; i<=count; i++) {
sig = order[i]
print "# Source: " sources[sig]
print keys[sig]
}
}')
MANUAL_KEYS=""
if [[ -f "${TARGET_FILE}" ]]; then
MANUAL_KEYS=$(awk -v begin="${BEGIN_MARKER}" -v end="${END_MARKER}" '
BEGIN { inside=0 }
$0 == begin { inside=1; next }
$0 == end { inside=0; next }
!inside { print $0 }
' "${TARGET_FILE}")
fi
MANAGED_SIGS_TMP=$(mktemp)
echo "${MANAGED_BLOCK}" | awk '/^[^#]/ {
n = split($0, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
if (sig != "") print sig
}' > "${MANAGED_SIGS_TMP}"
FINAL_MANUAL_KEYS=$(echo "${MANUAL_KEYS}" | awk -v sigs_file="${MANAGED_SIGS_TMP}" '
BEGIN {
while ((getline line < sigs_file) > 0) {
managed[line] = 1
}
close(sigs_file)
}
{
if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*#/) {
print $0
next
}
n = split($0, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
if (!(sig in managed)) {
print $0
}
}')
rm -f "${MANAGED_SIGS_TMP}"
{
if [[ -n "${MANAGED_BLOCK}" ]]; then
echo "${BEGIN_MARKER}"
echo "${MANAGED_BLOCK}"
echo "${END_MARKER}"
fi
echo "${FINAL_MANUAL_KEYS}"
} > "${TMP_FILE}"
echo "Validating proposed changes..."
VALID=1
while read -r line; do
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
if ! echo "${line}" | ssh-keygen -l -f - >/dev/null 2>&1; then
echo "ERROR: Invalid SSH key detected: ${line}" >&2
VALID=0
fi
done < "${TMP_FILE}"
if command -v authorized-keys-test >/dev/null 2>&1; then
if ! authorized-keys-test "${TMP_FILE}"; then
echo "ERROR: Proposed file failed authorized-keys-test." >&2
VALID=0
fi
fi
if [[ ${VALID} -eq 0 ]]; then
echo "Validation failed. Aborting." >&2
exit 1
fi
if [[ -f "${TARGET_FILE}" ]]; then
diff -u "${TARGET_FILE}" "${TMP_FILE}" || true
else
echo "Target file does not exist. Proposed content:"
cat "${TMP_FILE}"
fi
if [[ ${DRY_RUN} -eq 1 ]]; then
echo "Dry run complete. No changes made."
exit 0
fi
if [[ -t 0 ]]; then
echo -n "Apply these changes to ${TARGET_FILE}? [y/N] "
read -r response
elif [[ ! -t 0 ]]; then
# Read from pipe or file if provided
if ! read -r response; then
echo "Non-interactive shell detected and no input provided. Aborting."
exit 1
fi
fi
if [[ "${response}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
chmod 0600 "${TMP_FILE}"
mv "${TMP_FILE}" "${TARGET_FILE}"
echo "Changes applied successfully."
else
echo "Aborted."
exit 1
fi

View File

@@ -1,9 +1,6 @@
# General aliases, should only be sourced in interactive shells
# Try to keep in sync with ~/.config/fish/conf.d/aliases.fish
# Cryptsetup alias
alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Timestamp in a machine-sortable form
alias tstamp="date '+%Y%m%d-%H%M%S'"
@@ -13,12 +10,6 @@ alias mdcode="sed 's/^/ /'"
# Intel format plz
alias objdump="command objdump -M intel"
# Drop caches for swap issues
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
# get git working directory
alias gitroot="git rev-parse --show-toplevel"
@@ -31,20 +22,37 @@ alias ipy="ipython3 --no-banner"
# Skip the header on bc
alias bc="command bc -q"
# Get a decently readable df
alias dfh="df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Clear the GPG agent
alias clear-gpg-agent="echo RELOADAGENT | gpg-connect-agent"
# Battery details
alias bat-details='upower -i $(upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate='nvidia-settings --display=:0 -q RefreshRate -t'
# Earthly ssh
alias earthly='earthly --ssh-auth-sock ""'
# to clipboard
alias toclip='xclip -selection clipboard'
if [ "$(uname)" = "Linux" ]; then
# Cryptsetup alias
alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Drop caches for swap issues
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
# Get a decently readable df
alias dfh="df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Battery details
alias bat-details='upower -i $(upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate='nvidia-settings --display=:0 -q RefreshRate -t'
# to clipboard
alias toclip='xclip -selection clipboard'
elif [ "$(uname)" = "Darwin" ]; then
# Get a decently readable df
alias dfh="df -h"
# to clipboard
alias toclip='pbcopy'
fi

4
dotfiles/bxignore Normal file
View File

@@ -0,0 +1,4 @@
# Credentials
.ssh
Passwords.kdbx
Passwords.kdbx.age

View File

@@ -1,6 +1,3 @@
# Cryptsetup alias
alias luksFormat 'cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Timestamp in a machine-sortable form
alias tstamp "date '+%Y%m%d-%H%M%S'"
@@ -10,12 +7,6 @@ alias mdcode "sed 's/^/ /'"
# Intel format plz
alias objdump "command objdump -M intel"
# Drop caches for swap issues
alias drop_caches "echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp 'printf "%02.2f\n" (cat /sys/class/thermal/thermal_zone0/temp)e-3'
# get git working directory
alias gitroot "git rev-parse --show-toplevel"
@@ -28,23 +19,40 @@ alias ipy "ipython3 --no-banner"
# Skip the header on bc
alias bc "command bc -q"
# Get a decently readable df
alias dfh "df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Clear the GPG agent
alias clear-gpg-agent "echo RELOADAGENT | gpg-connect-agent"
# Battery details
alias bat-details 'upower -i (upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate 'nvidia-settings --display=:0 -q RefreshRate -t'
# Earthly ssh
alias earthly 'earthly --ssh-auth-sock ""'
# to clipboard
alias toclip 'xclip -selection clipboard'
if test (uname) = "Linux"
# Cryptsetup alias
alias luksFormat 'cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Drop caches for swap issues
alias drop_caches "echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp 'printf "%02.2f\n" (cat /sys/class/thermal/thermal_zone0/temp)e-3'
# Get a decently readable df
alias dfh "df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Battery details
alias bat-details 'upower -i (upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate 'nvidia-settings --display=:0 -q RefreshRate -t'
# to clipboard
alias toclip 'xclip -selection clipboard'
else if test (uname) = "Darwin"
# Get a decently readable df
alias dfh "df -h"
# to clipboard
alias toclip 'pbcopy'
end
# On some systems, bat is batcat
if not command -v bat >/dev/null 2>&1

View File

@@ -0,0 +1,95 @@
# Bridge to Gemini CLI Context Management (Zsh-backed)
# This allows using the Zsh implementation from Fish to avoid dual maintenance.
# Ensure the base context directory exists
if set -q XDG_CONFIG_HOME
set -gx GEMINI_CONTEXT_DIR $XDG_CONFIG_HOME/gemini
else
set -gx GEMINI_CONTEXT_DIR $HOME/.config/gemini
end
mkdir -p "$GEMINI_CONTEXT_DIR"
# Path to the source of truth
set -l gemini_zsh_script "$HOME/.skel/dotfiles/zshrc.d/gemini.zsh"
function _gemini_context_bridge
# Check if Zsh script exists
if not test -f "$gemini_zsh_script"
echo "Error: Gemini Zsh script not found at $gemini_zsh_script"
return 1
end
# We use a temporary file to reliably pass the environment variable back
set -l env_file (mktemp -t gemini_context.XXXXXX)
# Run zsh with -e (exit on error):
# 1. Source the context manager
# 2. Run the requested command with arguments
# 3. Write the resulting GEMINI_CLI_HOME to the temp file
command zsh -ec "
source '$gemini_zsh_script'
$argv
echo \"\$GEMINI_CLI_HOME\" > '$env_file'
"
set -l zsh_status $status
# Only sync environment and clean up on success
if test $zsh_status -eq 0
set -l new_home (cat $env_file | string trim)
if test -n "$new_home"
set -gx GEMINI_CLI_HOME "$new_home"
else
set -e GEMINI_CLI_HOME
end
end
rm -f $env_file
return $zsh_status
end
# Wrapper functions
function gemini-context-use
_gemini_context_bridge "gemini-context-use $argv"
end
function gemini-context-create
_gemini_context_bridge "gemini-context-create $argv"
end
function gemini-context-list
_gemini_context_bridge "gemini-context-list $argv"
end
function gemini-context-delete
_gemini_context_bridge "gemini-context-delete $argv"
end
function gemini-context-rename
_gemini_context_bridge "gemini-context-rename $argv"
end
function gemini-context-edit
_gemini_context_bridge "gemini-context-edit $argv"
end
function gemini-context-current
_gemini_context_bridge "gemini-context-current $argv"
end
function gemini-context-unset
_gemini_context_bridge "gemini-context-unset $argv"
end
# Aliases
alias gemctx='gemini-context-use'
# Completion
function _gemini_context_list
zsh -c "source '$gemini_zsh_script'; _gemini_context_list_internal"
end
complete -c gemini-context-use -f -a "(_gemini_context_list)"
complete -c gemctx -f -a "(_gemini_context_list)"
complete -c gemini-context-edit -f -a "(_gemini_context_list)"
complete -c gemini-context-delete -f -a "(_gemini_context_list)"
complete -c gemini-context-rename -f -a "(_gemini_context_list)"

View File

@@ -30,5 +30,10 @@ if status --is-interactive
install_fisher
end
# Want this at the bottom to put this path first
# Want these at the bottom to put them first in PATH
fish_add_path --move --path {$HOME}/bin
if test (uname) = "Darwin"
fish_add_path --move --path {$HOME}/bin/macos
else if test (uname) = "Linux"
fish_add_path --move --path {$HOME}/bin/linux
end

View File

@@ -10,7 +10,7 @@ uv_venv_auto = true
[tools]
age = "latest"
usage = "latest"
uv = "latest"
uv = "0.11.8"
[hooks]
postinstall = "mise sync python --uv"

View File

@@ -51,3 +51,4 @@ fi
"""
style = "bold blue"
format = "♊[$output](blue) "
shell = ["/bin/sh", "-c"]

View File

@@ -19,14 +19,17 @@
[difftool]
prompt = false
[difftool "difftastic"]
cmd = difft "$LOCAL" "$REMOTE"
[alias]
st = status
last = log -1 HEAD
# Thanks to
# http://durdn.com/blog/2012/11/22/must-have-git-aliases-advanced-examples/
logs = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate
logs = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate
lg = log -p
ll = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat
ll = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate --numstat
files = ls-files
ls = ls-files
lol = log --graph --pretty=format:'%C(yellow)%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cblue(%cr)%Creset' --abbrev-commit --date=relative
@@ -92,8 +95,12 @@
process = git-lfs filter-process
[include]
path = ~/.gitconfig.d/aliases
path = ~/.gitconfig.d/override
path = ~/.gitconfig.d/local
[includeIf "gitdir:~/personal/"]
[includeIf "gitdir/i:~/personal/"]
path = ~/.gitconfig.d/personal
[rerere]
enabled = true

View File

@@ -0,0 +1,9 @@
[alias]
commit-assumed = "!f() { \
file=\"$1\"; \
shift; \
git update-index --no-assume-unchanged \"$file\" && \
git add \"$file\" && \
git commit \"$@\" && \
git update-index --assume-unchanged \"$file\"; \
}; f"

View File

@@ -15,6 +15,7 @@ export DEBEMAIL="david@systemoverlord.com"
export DEBFULLNAME="David Tomaschik"
export LESS="-MR"
export QUOTING_STYLE="literal" # Coreutils quotes
export HOMEBREW_NO_ENV_HINTS=1
# Fix gnome-terminal
if [ "$TERM" = "xterm" ] && [ "$COLORTERM" = "gnome-terminal" ] ; then
@@ -97,31 +98,91 @@ export EARTHLY_SSH_AUTH_SOCK=""
# Handle SSH_AUTH_SOCK for tmux consistency
_SSH_AUTH_LINK="${HOME}/.ssh/ssh_auth_sock"
if [ -z "${SSH_AUTH_SOCK:-}" ] || [ ! -S "${SSH_AUTH_SOCK}" ] ; then
# Try to find a working GPG agent SSH socket if no agent is set or current is broken
if command -v gpgconf >/dev/null 2>&1; then
_GPG_SSH_SOCK=$(gpgconf --list-dirs agent-ssh-socket 2>/dev/null)
fi
# Fallback to common paths if gpgconf fails or isn't present
if [ -z "${_GPG_SSH_SOCK}" ] || [ ! -S "${_GPG_SSH_SOCK}" ]; then
_GPG_SSH_SOCK="${GNUPGHOME:-$HOME/.gnupg}/S.gpg-agent.ssh"
[ -S "$_GPG_SSH_SOCK" ] || _GPG_SSH_SOCK="/run/user/$(id -u)/gnupg/S.gpg-agent.ssh"
fi
if [ -S "${_GPG_SSH_SOCK}" ] ; then
export SSH_AUTH_SOCK="$_GPG_SSH_SOCK"
# Helper to check if a path is our link or points to it
_is_link_path() {
[ -z "$1" ] && return 1
[ "$1" = "${_SSH_AUTH_LINK}" ] && return 0
if [ -L "$1" ] && command -v readlink >/dev/null 2>&1; then
_T=$(readlink "$1")
# Handle relative symlinks
case "${_T}" in /*) ;; *) _T="$(dirname "$1")/${_T}" ;; esac
[ "${_T}" = "${_SSH_AUTH_LINK}" ] && return 0
fi
unset _GPG_SSH_SOCK
return 1
}
_CANDIDATE=""
# 1. If current environment has a valid socket that is NOT our link, it's a prime candidate
# (e.g. fresh SSH login: sshd sets SSH_AUTH_SOCK to the raw forwarded socket before ssh/rc
# rewrites it to the stable symlink; the shell inherits the original raw path).
if [ -S "${SSH_AUTH_SOCK:-}" ] && ! _is_link_path "${SSH_AUTH_SOCK}"; then
_CANDIDATE="${SSH_AUTH_SOCK}"
fi
# If we have a valid socket but it's not our stable link, sync the link and use it.
# This ensures tmux (using the static path) always finds the most recent agent.
if [ -S "${SSH_AUTH_SOCK:-}" ] && [ "${SSH_AUTH_SOCK}" != "${_SSH_AUTH_LINK}" ] ; then
[ -d "$(dirname "${_SSH_AUTH_LINK}")" ] || mkdir -p "$(dirname "${_SSH_AUTH_LINK}")"
ln -sf "${SSH_AUTH_SOCK}" "${_SSH_AUTH_LINK}"
# 2. Only look for a system agent if the stable link is already broken. If the link is
# valid (e.g. a tmux pane where SSH_AUTH_SOCK points to our symlink which ssh/rc just
# updated to the forwarded socket), leave it alone — don't clobber it with a local agent.
if [ -z "${_CANDIDATE}" ] && [ ! -S "${_SSH_AUTH_LINK}" ]; then
_FOUND=""
if [ "$(uname)" = "Darwin" ]; then
_FOUND=$(launchctl getenv SSH_AUTH_SOCK 2>/dev/null)
elif command -v systemctl >/dev/null 2>&1; then
# Query systemd socket units directly to avoid environment overrides.
for _u in ssh-agent.socket openssh-agent.socket gcr-ssh-agent.socket gpg-agent-ssh.socket; do
_P=$(systemctl --user show "${_u}" -p Listen 2>/dev/null | cut -d= -f2- | cut -d' ' -f1)
if [ -S "${_P}" ]; then
_FOUND="${_P}"
break
fi
done
# Fallback to systemd environment
if [ -z "${_FOUND}" ] || _is_link_path "${_FOUND}"; then
_FOUND=$(systemctl --user show-environment 2>/dev/null | grep "^SSH_AUTH_SOCK=" | cut -d= -f2-)
fi
fi
if [ -S "${_FOUND}" ] && ! _is_link_path "${_FOUND}"; then
_CANDIDATE="${_FOUND}"
fi
fi
# 3. Last resort: search common paths if we still don't have a candidate and the link is broken.
if [ ! -S "${_CANDIDATE}" ] && [ ! -S "${_SSH_AUTH_LINK}" ]; then
_U=$(id -u)
for _p in "/run/user/${_U}/keyring/ssh" "/run/user/${_U}/ssh-agent.socket" "/run/user/${_U}/openssh_agent" "/run/user/${_U}/gnupg/S.gpg-agent.ssh"; do
if [ -S "${_p}" ] && ! _is_link_path "${_p}"; then
_CANDIDATE="${_p}"
break
fi
done
if [ -z "${_CANDIDATE}" ]; then
# Build search path list based on what actually exists
_SEARCH=""
for _d in "/run/user/${_U}" /tmp; do [ -d "${_d}" ] && _SEARCH="${_SEARCH} ${_d}"; done
if [ -n "${_SEARCH}" ]; then
_CANDIDATE=$(find ${_SEARCH} -maxdepth 2 -type s -name 'agent.*' 2>/dev/null | grep -F -v "${_SSH_AUTH_LINK}" | head -n 1)
fi
fi
fi
# 4. Sync the stable link if we found a valid "real" socket.
if [ -S "${_CANDIDATE}" ] && ! _is_link_path "${_CANDIDATE}"; then
mkdir -p "$(dirname "${_SSH_AUTH_LINK}")"
ln -sf "${_CANDIDATE}" "${_SSH_AUTH_LINK}"
export SSH_AUTH_SOCK="${_SSH_AUTH_LINK}"
# Update systemd if present to keep everything in sync
if command -v systemctl >/dev/null 2>&1; then
systemctl --user set-environment SSH_AUTH_SOCK="${_SSH_AUTH_LINK}" 2>/dev/null
fi
elif [ -S "${_SSH_AUTH_LINK}" ]; then
# If we found nothing better but the link is valid, use it.
export SSH_AUTH_SOCK="${_SSH_AUTH_LINK}"
fi
unset _SSH_AUTH_LINK
unset _SSH_AUTH_LINK _CANDIDATE _FOUND _T _P _U _u _SEARCH _d
unset -f _is_link_path
# Setup XDG-like dirs on MacOS
# Based on https://leebyron.com/til/mac-xdg/
@@ -133,6 +194,9 @@ if [ "$(uname)" = "Darwin" ] ; then
# Using id -u for better POSIX compatibility than $UID
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-$TMPDIR/runtime-$(id -u)}"
export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
export PATH="${HOME}/bin/macos:${PATH}"
elif [ "$(uname)" = "Linux" ] ; then
export PATH="${HOME}/bin/linux:${PATH}"
fi
if test -e "$HOME/.localenv"; then

View File

@@ -2,19 +2,19 @@
# Roughly based on this article:
# https://werat.github.io/2017/02/04/tmux-ssh-agent-forwarding.html
#
# NOTE: this file is executed by sshd as a child process, NOT sourced by the
# user's shell. Any variable assignments or exports here have no effect on the
# shell environment the user will land in.
REMOTE_LINK="${HOME}/.ssh/ssh_auth_sock"
if [ -S "${SSH_AUTH_SOCK}" ] ; then
SSH_REMOTE_AUTH_SOCK="${SSH_AUTH_SOCK}"
export SSH_REMOTE_AUTH_SOCK
# Always update the symlink to the latest session's socket.
# This ensures that tmux (which uses the static path) always points to a
# current agent.
mkdir -p "$(dirname "${REMOTE_LINK}")"
ln -sf "${SSH_AUTH_SOCK}" "${REMOTE_LINK}"
SSH_AUTH_SOCK="${REMOTE_LINK}"
export SSH_AUTH_SOCK
fi
# if stdin is a tty, don't do the cookie step

View File

@@ -45,7 +45,7 @@ set -g window-status-current-style fg=colour235,bg=colour33,bold
set -g status-interval 60
set -g status-left-length 30
set -g status-left '/#h: #S/ '
set -g status-right '#{?pane_title,/#{pane_title}/ ,}#(cut -d " " -f 1-3 /proc/loadavg)#[default] #[fg=colour166]%H:%M#[default]'
set -g status-right '#{?pane_title,/#{pane_title}/ ,}#(uptime | rev | cut -d":" -f1 | rev | sed s/,//g)#[default] #[fg=colour166]%H:%M#[default]'
# Advanced mouse mode from http://tangledhelix.com/blog/2012/07/16/tmux-and-mouse-mode/
# Toggle mouse on
@@ -65,8 +65,9 @@ bind M \
display 'Mouse: OFF'
# tmux X clipboard integration
bind C-c run "tmux show-buffer | xsel -i -b"
bind C-v run "tmux set-buffer -- \"$(xsel -o -b)\"; tmux paste-buffer"
if-shell 'test "$(uname)" = "Darwin"' \
'bind C-c run "tmux show-buffer | pbcopy"; bind C-v run "tmux set-buffer -- \"$(pbpaste)\"; tmux paste-buffer"' \
'bind C-c run "tmux show-buffer | xsel -i -b"; bind C-v run "tmux set-buffer -- \"$(xsel -o -b)\"; tmux paste-buffer"'
# List of plugins
set -g @plugin 'tmux-plugins/tpm'

View File

@@ -1,41 +0,0 @@
#
# weechat -- alias.conf
#
[cmd]
AAWAY = "allserv /away"
AME = "allchan /me"
AMSG = "allchan /msg *"
ANICK = "allserv /nick"
BEEP = "print -beep"
BYE = "quit"
C = "buffer clear"
CHAT = "dcc chat"
CL = "buffer clear"
CLOSE = "buffer close"
EXIT = "quit"
IG = "ignore"
J = "join"
K = "kick"
KB = "kickban"
LEAVE = "part"
M = "msg"
MSGBUF = "command -buffer $1 * /input send $2-"
MUB = "unban *"
N = "names"
Q = "query"
REDRAW = "window refresh"
SAY = "msg *"
SIGNOFF = "quit"
T = "topic"
UB = "unban"
UMODE = "mode $nick"
V = "command core version"
W = "who"
WC = "window merge"
WI = "whois"
WII = "whois $1 $1"
WW = "whowas"
[completion]
MSGBUF = "%(buffers_plugins_names)"

View File

@@ -1,20 +0,0 @@
#
# weechat -- aspell.conf
#
[color]
misspelled = lightred
suggestions = default
[check]
commands = "ame,amsg,away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic"
default_dict = ""
during_search = off
enabled = off
real_time = off
suggestions = -1
word_min_length = 2
[dict]
[option]

View File

@@ -1,11 +0,0 @@
#
# weechat -- charset.conf
#
[default]
decode = "iso-8859-1"
encode = ""
[decode]
[encode]

View File

@@ -1,11 +0,0 @@
#
# weechat -- exec.conf
#
[command]
default_options = ""
purge_delay = 0
[color]
flag_finished = lightred
flag_running = lightgreen

View File

@@ -1,377 +0,0 @@
#
# weechat -- irc.conf
#
[look]
buffer_open_before_autojoin = on
buffer_open_before_join = off
buffer_switch_autojoin = on
buffer_switch_join = on
color_nicks_in_names = off
color_nicks_in_nicklist = off
color_nicks_in_server_messages = on
color_pv_nick_like_channel = on
ctcp_time_format = "%a, %d %b %Y %T %z"
display_away = local
display_ctcp_blocked = on
display_ctcp_reply = on
display_ctcp_unknown = on
display_host_join = on
display_host_join_local = on
display_host_quit = on
display_join_message = "329,332,333,366"
display_old_topic = on
display_pv_away_once = on
display_pv_back = on
highlight_channel = "$nick"
highlight_pv = "$nick"
highlight_server = "$nick"
highlight_tags_restrict = "irc_privmsg,irc_notice"
item_channel_modes_hide_args = "k"
item_display_server = buffer_plugin
item_nick_modes = on
item_nick_prefix = on
join_auto_add_chantype = off
msgbuffer_fallback = current
new_channel_position = none
new_pv_position = none
nick_completion_smart = speakers
nick_mode = prefix
nick_mode_empty = off
nicks_hide_password = "nickserv"
notice_as_pv = auto
notice_welcome_redirect = on
notice_welcome_tags = ""
notify_tags_ison = "notify_message"
notify_tags_whois = "notify_message"
part_closes_buffer = on
pv_buffer = independent
pv_tags = "notify_private"
raw_messages = 256
server_buffer = merge_with_core
smart_filter = on
smart_filter_delay = 5
smart_filter_join = on
smart_filter_join_unmask = 30
smart_filter_mode = "+"
smart_filter_nick = on
smart_filter_quit = on
temporary_servers = off
topic_strip_colors = off
[color]
input_nick = lightcyan
item_channel_modes = default
item_lag_counting = default
item_lag_finished = yellow
item_nick_modes = default
message_join = green
message_quit = red
mirc_remap = "1,-1:darkgray"
nick_prefixes = "q:lightred;a:lightcyan;o:lightgreen;h:lightmagenta;v:yellow;*:lightblue"
notice = green
reason_quit = default
topic_current = default
topic_new = white
topic_old = default
[network]
autoreconnect_delay_growing = 2
autoreconnect_delay_max = 600
ban_mask_default = "*!$ident@$host"
channel_encode = off
colors_receive = on
colors_send = on
lag_check = 60
lag_max = 1800
lag_min_show = 500
lag_reconnect = 0
lag_refresh_interval = 1
notify_check_ison = 1
notify_check_whois = 5
sasl_fail_unavailable = on
send_unknown_commands = off
whois_double_nick = off
[msgbuffer]
[ctcp]
[ignore]
[server_default]
addresses = ""
anti_flood_prio_high = 2
anti_flood_prio_low = 2
autoconnect = off
autojoin = ""
autoreconnect = on
autoreconnect_delay = 10
autorejoin = off
autorejoin_delay = 30
away_check = 0
away_check_max_nicks = 25
capabilities = ""
command = ""
command_delay = 0
connection_timeout = 60
ipv6 = on
local_hostname = ""
msg_kick = ""
msg_part = "WeeChat ${info:version}"
msg_quit = "WeeChat ${info:version}"
nicks = "Matir,Matir~,Matir[]"
nicks_alternate = on
notify = ""
password = ""
proxy = ""
realname = ""
sasl_fail = continue
sasl_key = ""
sasl_mechanism = plain
sasl_password = ""
sasl_timeout = 15
sasl_username = ""
ssl = off
ssl_cert = ""
ssl_dhkey_size = 2048
ssl_fingerprint = ""
ssl_priorities = "NORMAL"
ssl_verify = on
username = "matir"
[server]
freenode.addresses = "chat.freenode.net/7000"
freenode.proxy
freenode.ipv6
freenode.ssl = on
freenode.ssl_cert = "%h/certs/freenode-matir.pem"
freenode.ssl_priorities
freenode.ssl_dhkey_size
freenode.ssl_fingerprint
freenode.ssl_verify = on
freenode.password
freenode.capabilities
freenode.sasl_mechanism
freenode.sasl_username
freenode.sasl_password
freenode.sasl_key
freenode.sasl_timeout
freenode.sasl_fail
freenode.autoconnect = on
freenode.autoreconnect
freenode.autoreconnect_delay
freenode.nicks = "Matir,Matir~"
freenode.nicks_alternate
freenode.username
freenode.realname
freenode.local_hostname
freenode.command
freenode.command_delay
freenode.autojoin = "#kali-linux,#openvpn,#radare,#vulnhub,#offsec,#offtopicsec,##ctfcompetition,#dc404,#droidsec"
freenode.autorejoin
freenode.autorejoin_delay
freenode.connection_timeout
freenode.anti_flood_prio_high
freenode.anti_flood_prio_low
freenode.away_check
freenode.away_check_max_nicks
freenode.msg_kick
freenode.msg_part
freenode.msg_quit
freenode.notify
hak5.addresses = "irc.hak5.org/6697"
hak5.proxy
hak5.ipv6
hak5.ssl = on
hak5.ssl_cert = "%h/certs/freenode-matir.pem"
hak5.ssl_priorities
hak5.ssl_dhkey_size
hak5.ssl_fingerprint
hak5.ssl_verify = off
hak5.password
hak5.capabilities
hak5.sasl_mechanism
hak5.sasl_username
hak5.sasl_password
hak5.sasl_key
hak5.sasl_timeout
hak5.sasl_fail
hak5.autoconnect = on
hak5.autoreconnect
hak5.autoreconnect_delay
hak5.nicks
hak5.nicks_alternate
hak5.username
hak5.realname
hak5.local_hostname
hak5.command
hak5.command_delay
hak5.autojoin = "#hak5,#pineapple,#ducky,#SDR,#lanturtle,#bashbunny"
hak5.autorejoin
hak5.autorejoin_delay
hak5.connection_timeout
hak5.anti_flood_prio_high
hak5.anti_flood_prio_low
hak5.away_check
hak5.away_check_max_nicks
hak5.msg_kick
hak5.msg_part
hak5.msg_quit
hak5.notify
rpisec.addresses = "irc.rpis.ec/6697"
rpisec.proxy
rpisec.ipv6
rpisec.ssl = on
rpisec.ssl_cert
rpisec.ssl_priorities
rpisec.ssl_dhkey_size
rpisec.ssl_fingerprint
rpisec.ssl_verify = on
rpisec.password
rpisec.capabilities
rpisec.sasl_mechanism
rpisec.sasl_username
rpisec.sasl_password
rpisec.sasl_key
rpisec.sasl_timeout
rpisec.sasl_fail
rpisec.autoconnect = on
rpisec.autoreconnect
rpisec.autoreconnect_delay
rpisec.nicks
rpisec.nicks_alternate
rpisec.username
rpisec.realname
rpisec.local_hostname
rpisec.command
rpisec.command_delay
rpisec.autojoin = "#rpisec"
rpisec.autorejoin
rpisec.autorejoin_delay
rpisec.connection_timeout
rpisec.anti_flood_prio_high
rpisec.anti_flood_prio_low
rpisec.away_check
rpisec.away_check_max_nicks
rpisec.msg_kick
rpisec.msg_part
rpisec.msg_quit
rpisec.notify
overthewire.addresses = "ircs.overthewire.org/6697"
overthewire.proxy
overthewire.ipv6
overthewire.ssl = on
overthewire.ssl_cert = "%h/certs/freenode-matir.pem"
overthewire.ssl_priorities
overthewire.ssl_dhkey_size
overthewire.ssl_fingerprint
overthewire.ssl_verify = on
overthewire.password
overthewire.capabilities
overthewire.sasl_mechanism
overthewire.sasl_username
overthewire.sasl_password
overthewire.sasl_key
overthewire.sasl_timeout
overthewire.sasl_fail
overthewire.autoconnect = on
overthewire.autoreconnect
overthewire.autoreconnect_delay
overthewire.nicks
overthewire.nicks_alternate
overthewire.username
overthewire.realname
overthewire.local_hostname
overthewire.command
overthewire.command_delay
overthewire.autojoin = "#wargames,#social,#amateria,#io"
overthewire.autorejoin
overthewire.autorejoin_delay
overthewire.connection_timeout
overthewire.anti_flood_prio_high
overthewire.anti_flood_prio_low
overthewire.away_check
overthewire.away_check_max_nicks
overthewire.msg_kick
overthewire.msg_part
overthewire.msg_quit
overthewire.notify
hackint.addresses = "irc.hackint.org/9999"
hackint.proxy
hackint.ipv6
hackint.ssl = on
hackint.ssl_cert
hackint.ssl_priorities
hackint.ssl_dhkey_size
hackint.ssl_fingerprint
hackint.ssl_verify = on
hackint.password
hackint.capabilities
hackint.sasl_mechanism
hackint.sasl_username
hackint.sasl_password
hackint.sasl_key
hackint.sasl_timeout
hackint.sasl_fail
hackint.autoconnect = on
hackint.autoreconnect
hackint.autoreconnect_delay
hackint.nicks
hackint.nicks_alternate
hackint.username
hackint.realname
hackint.local_hostname
hackint.command
hackint.command_delay
hackint.autojoin
hackint.autorejoin
hackint.autorejoin_delay
hackint.connection_timeout
hackint.anti_flood_prio_high
hackint.anti_flood_prio_low
hackint.away_check
hackint.away_check_max_nicks
hackint.msg_kick
hackint.msg_part
hackint.msg_quit
hackint.notify
afternet.addresses = "irc.afternet.org/6697"
afternet.proxy
afternet.ipv6
afternet.ssl = on
afternet.ssl_cert
afternet.ssl_priorities
afternet.ssl_dhkey_size
afternet.ssl_fingerprint
afternet.ssl_verify = on
afternet.password
afternet.capabilities
afternet.sasl_mechanism
afternet.sasl_username
afternet.sasl_password
afternet.sasl_key
afternet.sasl_timeout
afternet.sasl_fail
afternet.autoconnect = on
afternet.autoreconnect
afternet.autoreconnect_delay
afternet.nicks
afternet.nicks_alternate
afternet.username
afternet.realname
afternet.local_hostname
afternet.command
afternet.command_delay
afternet.autojoin = "#eevblog"
afternet.autorejoin
afternet.autorejoin_delay
afternet.connection_timeout
afternet.anti_flood_prio_high
afternet.anti_flood_prio_low
afternet.away_check
afternet.away_check_max_nicks
afternet.msg_kick
afternet.msg_part
afternet.msg_quit
afternet.notify

View File

@@ -1,14 +0,0 @@
#!/bin/bash
# Update the weechat SSL key. Should be called from cron via sudo.
eval WEEDIR="$(printf "~%q/.weechat/" "${SUDO_USER}")"
LIVEKEY="${WEEDIR}/ssl/relay.pem"
certbot renew -q
cat /etc/letsencrypt/live/$(hostname -f)/{privkey,fullchain}.pem > \
${LIVEKEY}
chown ${SUDO_USER}:$(id -gn ${SUDO_USER}) ${LIVEKEY}
for fifo in ${WEEDIR}/weechat_fifo* ; do
echo '*/relay sslcertkey' > ${fifo}
done

View File

@@ -1,26 +0,0 @@
#
# weechat -- logger.conf
#
[look]
backlog = 20
[color]
backlog_end = default
backlog_line = default
[file]
auto_log = on
flush_delay = 120
info_lines = off
mask = "$plugin.$name.weechatlog"
name_lower_case = on
nick_prefix = ""
nick_suffix = ""
path = "%h/logs/"
replacement_char = "_"
time_format = "%Y-%m-%d %H:%M:%S"
[level]
[mask]

View File

@@ -1,15 +0,0 @@
#
# weechat -- plugins.conf
#
[var]
fifo.fifo = "on"
guile.check_license = "off"
javascript.check_license = "off"
lua.check_license = "off"
perl.check_license = "off"
python.check_license = "off"
ruby.check_license = "off"
tcl.check_license = "off"
[desc]

View File

@@ -1,42 +0,0 @@
#
# weechat -- relay.conf
#
[look]
auto_open_buffer = on
raw_messages = 256
[color]
client = cyan
status_active = lightblue
status_auth_failed = lightred
status_connecting = yellow
status_disconnected = lightred
status_waiting_auth = brown
text = default
text_bg = default
text_selected = white
[network]
allow_empty_password = off
allowed_ips = ""
bind_address = ""
clients_purge_delay = 0
compression_level = 6
ipv6 = on
max_clients = 5
password = "${sec.data.relay_password}"
ssl_cert_key = "%h/ssl/relay.pem"
ssl_priorities = "NORMAL:-VERS-SSL3.0"
websocket_allowed_origins = ""
[irc]
backlog_max_minutes = 1440
backlog_max_number = 256
backlog_since_last_disconnect = on
backlog_since_last_message = off
backlog_tags = "irc_privmsg"
backlog_time_format = "[%H:%M] "
[port]
ssl.weechat = 9001

View File

@@ -1,50 +0,0 @@
#
# weechat -- script.conf
#
[look]
columns = "%s %n %V %v %u | %d | %t"
diff_color = on
diff_command = "auto"
display_source = on
quiet_actions = on
sort = "p,n"
translate_description = on
use_keys = on
[color]
status_autoloaded = cyan
status_held = white
status_installed = lightcyan
status_obsolete = lightmagenta
status_popular = yellow
status_running = lightgreen
status_unknown = lightred
text = default
text_bg = default
text_bg_selected = red
text_date = default
text_date_selected = white
text_delimiters = default
text_description = default
text_description_selected = white
text_extension = default
text_extension_selected = white
text_name = cyan
text_name_selected = lightcyan
text_selected = white
text_tags = brown
text_tags_selected = yellow
text_version = magenta
text_version_loaded = default
text_version_loaded_selected = white
text_version_selected = lightmagenta
[scripts]
autoload = on
cache_expire = 1440
download_timeout = 30
hold = ""
path = "%h/script"
url = "http://weechat.org/files/plugins.xml.gz"
url_force_https = on

View File

@@ -1,13 +0,0 @@
#
# weechat -- sec.conf
#
[crypt]
cipher = aes256
hash_algo = sha256
passphrase_file = "~/.weechat-passphrase"
salt = on
[data]
__passphrase__ = on
relay_password = "D1FD30C08951B1A5BCBBB7EE6AAFB6AF9B86017B353182A1CA8826D5A98EB88E7E723591C544FC41A6913EA67E8764E50BDD8A5AD3D0A0"

View File

@@ -1,52 +0,0 @@
#
# weechat -- trigger.conf
#
[look]
enabled = on
monitor_strip_colors = off
[color]
flag_command = lightgreen
flag_conditions = yellow
flag_post_action = lightblue
flag_regex = lightcyan
flag_return_code = lightmagenta
regex = white
replace = cyan
trigger = green
trigger_disabled = red
[trigger]
beep.arguments = ""
beep.command = "/print -beep"
beep.conditions = "${tg_highlight} || ${tg_msg_pv}"
beep.enabled = on
beep.hook = print
beep.post_action = none
beep.regex = ""
beep.return_code = ok
cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth"
cmd_pass.command = ""
cmd_pass.conditions = ""
cmd_pass.enabled = on
cmd_pass.hook = modifier
cmd_pass.post_action = none
cmd_pass.regex = "==^((/(msg|quote) +nickserv +(id|identify|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/set +[^ ]*password[^ ]* +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==$1$.*+"
cmd_pass.return_code = ok
msg_auth.arguments = "5000|irc_message_auth"
msg_auth.command = ""
msg_auth.conditions = ""
msg_auth.enabled = on
msg_auth.hook = modifier
msg_auth.post_action = none
msg_auth.regex = "==^(.*(id|identify|register|ghost +[^ ]+|release +[^ ]+) +)(.*)==$1$.*+"
msg_auth.return_code = ok
server_pass.arguments = "5000|input_text_display;5000|history_add"
server_pass.command = ""
server_pass.conditions = ""
server_pass.enabled = on
server_pass.hook = modifier
server_pass.post_action = none
server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==$1$.*4$5"
server_pass.return_code = ok

View File

@@ -1,595 +0,0 @@
#
# weechat -- weechat.conf
#
[debug]
[startup]
command_after_plugins = ""
command_before_plugins = ""
display_logo = on
display_version = on
sys_rlimit = ""
[look]
align_end_of_lines = message
bar_more_down = "++"
bar_more_left = "<<"
bar_more_right = ">>"
bar_more_up = "--"
bare_display_exit_on_input = on
bare_display_time_format = "%H:%M"
buffer_auto_renumber = on
buffer_notify_default = all
buffer_position = end
buffer_search_case_sensitive = off
buffer_search_force_default = off
buffer_search_regex = off
buffer_search_where = prefix_message
buffer_time_format = "%H:%M:%S"
color_basic_force_bold = off
color_inactive_buffer = on
color_inactive_message = on
color_inactive_prefix = on
color_inactive_prefix_buffer = on
color_inactive_time = off
color_inactive_window = on
color_nick_offline = off
color_pairs_auto_reset = 5
color_real_white = off
command_chars = ""
command_incomplete = off
confirm_quit = off
confirm_upgrade = off
day_change = on
day_change_message_1date = "-- %a, %d %b %Y --"
day_change_message_2dates = "-- %%a, %%d %%b %%Y (%a, %d %b %Y) --"
eat_newline_glitch = off
emphasized_attributes = ""
highlight = ""
highlight_regex = ""
highlight_tags = ""
hotlist_add_conditions = "${buffer.num_displayed} == 0 && ${priority} >= 1"
hotlist_buffer_separator = ", "
hotlist_count_max = 0
hotlist_count_min_msg = 2
hotlist_names_count = 10000
hotlist_names_length = 0
hotlist_names_level = 14
hotlist_names_merged_buffers = off
hotlist_prefix = "Act: "
hotlist_remove = merged
hotlist_short_names = on
hotlist_sort = group_time_asc
hotlist_suffix = ""
hotlist_unique_numbers = on
input_cursor_scroll = 20
input_share = none
input_share_overwrite = off
input_undo_max = 32
item_away_message = on
item_buffer_filter = "*"
item_buffer_zoom = "!"
item_mouse_status = "M"
item_time_format = "%H:%M"
jump_current_to_previous_buffer = on
jump_previous_buffer_when_closing = on
jump_smart_back_to_buffer = on
key_bind_safe = on
key_grab_delay = 800
mouse = off
mouse_timer_delay = 100
nick_color_force = ""
nick_color_hash = djb2
nick_color_stop_chars = "_|["
nick_prefix = ""
nick_suffix = ""
paste_auto_add_newline = on
paste_bracketed = on
paste_bracketed_timer_delay = 10
paste_max_lines = 1
prefix_action = " *"
prefix_align = right
prefix_align_max = 15
prefix_align_min = 0
prefix_align_more = "+"
prefix_align_more_after = on
prefix_buffer_align = right
prefix_buffer_align_max = 0
prefix_buffer_align_more = "+"
prefix_buffer_align_more_after = on
prefix_error = "=!="
prefix_join = "-->"
prefix_network = "--"
prefix_quit = "<--"
prefix_same_nick = ""
prefix_suffix = "|"
quote_nick_prefix = "<"
quote_nick_suffix = ">"
quote_time_format = "%H:%M:%S"
read_marker = line
read_marker_always_show = off
read_marker_string = "- "
save_config_on_exit = on
save_layout_on_exit = none
scroll_amount = 3
scroll_bottom_after_switch = off
scroll_page_percent = 100
search_text_not_found_alert = on
separator_horizontal = "-"
separator_vertical = ""
tab_width = 1
time_format = "%a, %d %b %Y %T"
window_auto_zoom = off
window_separator_horizontal = on
window_separator_vertical = on
window_title = "irc"
word_chars_highlight = "!\u00A0,-,_,|,alnum"
word_chars_input = "!\u00A0,-,_,|,alnum"
[palette]
[color]
bar_more = lightmagenta
chat = default
chat_bg = default
chat_buffer = white
chat_channel = white
chat_day_change = cyan
chat_delimiters = green
chat_highlight = yellow
chat_highlight_bg = magenta
chat_host = cyan
chat_inactive_buffer = default
chat_inactive_window = default
chat_nick = lightcyan
chat_nick_colors = "cyan,magenta,green,brown,lightblue,default,lightcyan,lightmagenta,lightgreen,blue"
chat_nick_offline = default
chat_nick_offline_highlight = default
chat_nick_offline_highlight_bg = blue
chat_nick_other = cyan
chat_nick_prefix = green
chat_nick_self = white
chat_nick_suffix = green
chat_prefix_action = white
chat_prefix_buffer = brown
chat_prefix_buffer_inactive_buffer = default
chat_prefix_error = yellow
chat_prefix_join = lightgreen
chat_prefix_more = lightmagenta
chat_prefix_network = magenta
chat_prefix_quit = lightred
chat_prefix_suffix = green
chat_read_marker = magenta
chat_read_marker_bg = default
chat_server = brown
chat_tags = red
chat_text_found = yellow
chat_text_found_bg = lightmagenta
chat_time = default
chat_time_delimiters = brown
chat_value = cyan
chat_value_null = blue
emphasized = yellow
emphasized_bg = magenta
input_actions = lightgreen
input_text_not_found = red
item_away = yellow
nicklist_away = cyan
nicklist_group = green
separator = blue
status_count_highlight = magenta
status_count_msg = brown
status_count_other = default
status_count_private = green
status_data_highlight = lightmagenta
status_data_msg = yellow
status_data_other = default
status_data_private = lightgreen
status_filter = green
status_more = yellow
status_mouse = green
status_name = white
status_name_ssl = lightgreen
status_nicklist_count = default
status_number = yellow
status_time = default
[completion]
base_word_until_cursor = on
command_inline = on
default_template = "%(nicks)|%(irc_channels)"
nick_add_space = on
nick_completer = ":"
nick_first_only = off
nick_ignore_chars = "[]`_-^"
partial_completion_alert = on
partial_completion_command = off
partial_completion_command_arg = off
partial_completion_count = on
partial_completion_other = off
[history]
display_default = 5
max_buffer_lines_minutes = 0
max_buffer_lines_number = 4096
max_commands = 100
max_visited_buffers = 50
[proxy]
[network]
connection_timeout = 60
gnutls_ca_file = "~/.weechat/certs/ca-certificates.crt"
gnutls_handshake_timeout = 30
proxy_curl = ""
[plugin]
autoload = "*"
debug = off
extension = ".so,.dll"
path = "%h/plugins"
save_config_on_unload = on
[bar]
input.color_bg = default
input.color_delim = cyan
input.color_fg = default
input.conditions = ""
input.filling_left_right = vertical
input.filling_top_bottom = horizontal
input.hidden = off
input.items = "[input_prompt]+(away),[input_search],[input_paste],input_text"
input.position = bottom
input.priority = 1000
input.separator = off
input.size = 1
input.size_max = 0
input.type = window
nicklist.color_bg = default
nicklist.color_delim = cyan
nicklist.color_fg = default
nicklist.conditions = "${nicklist}"
nicklist.filling_left_right = vertical
nicklist.filling_top_bottom = columns_vertical
nicklist.hidden = on
nicklist.items = "buffer_nicklist"
nicklist.position = right
nicklist.priority = 200
nicklist.separator = on
nicklist.size = 0
nicklist.size_max = 0
nicklist.type = window
status.color_bg = 0
status.color_delim = cyan
status.color_fg = default
status.conditions = ""
status.filling_left_right = vertical
status.filling_top_bottom = horizontal
status.hidden = off
status.items = "[time],[buffer_plugin],buffer_number+:+buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,[lag],[hotlist],completion,scroll"
status.position = bottom
status.priority = 500
status.separator = off
status.size = 2
status.size_max = 0
status.type = window
title.color_bg = 0
title.color_delim = cyan
title.color_fg = default
title.conditions = ""
title.filling_left_right = vertical
title.filling_top_bottom = horizontal
title.hidden = off
title.items = "buffer_title"
title.position = top
title.priority = 500
title.separator = off
title.size = 1
title.size_max = 0
title.type = window
[layout]
[notify]
[filter]
irc_smart = on;*;irc_smart_filter;*
[key]
ctrl-? = "/input delete_previous_char"
ctrl-A = "/input move_beginning_of_line"
ctrl-B = "/input move_previous_char"
ctrl-C_ = "/input insert \x1F"
ctrl-Cb = "/input insert \x02"
ctrl-Cc = "/input insert \x03"
ctrl-Ci = "/input insert \x1D"
ctrl-Co = "/input insert \x0F"
ctrl-Cv = "/input insert \x16"
ctrl-D = "/input delete_next_char"
ctrl-E = "/input move_end_of_line"
ctrl-F = "/input move_next_char"
ctrl-H = "/input delete_previous_char"
ctrl-I = "/input complete_next"
ctrl-J = "/input return"
ctrl-K = "/input delete_end_of_line"
ctrl-L = "/window refresh"
ctrl-M = "/input return"
ctrl-N = "/buffer +1"
ctrl-P = "/buffer -1"
ctrl-R = "/input search_text"
ctrl-Sctrl-U = "/input set_unread"
ctrl-T = "/input transpose_chars"
ctrl-U = "/input delete_beginning_of_line"
ctrl-W = "/input delete_previous_word"
ctrl-X = "/input switch_active_buffer"
ctrl-Y = "/input clipboard_paste"
meta-meta2-1~ = "/window scroll_top"
meta-meta2-23~ = "/bar scroll nicklist * b"
meta-meta2-24~ = "/bar scroll nicklist * e"
meta-meta2-4~ = "/window scroll_bottom"
meta-meta2-5~ = "/window scroll_up"
meta-meta2-6~ = "/window scroll_down"
meta-meta2-7~ = "/window scroll_top"
meta-meta2-8~ = "/window scroll_bottom"
meta-meta2-A = "/buffer -1"
meta-meta2-B = "/buffer +1"
meta-meta2-C = "/buffer +1"
meta-meta2-D = "/buffer -1"
meta-- = "/filter toggle @"
meta-/ = "/input jump_last_buffer_displayed"
meta-0 = "/buffer *10"
meta-1 = "/buffer *1"
meta-2 = "/buffer *2"
meta-3 = "/buffer *3"
meta-4 = "/buffer *4"
meta-5 = "/buffer *5"
meta-6 = "/buffer *6"
meta-7 = "/buffer *7"
meta-8 = "/buffer *8"
meta-9 = "/buffer *9"
meta-< = "/input jump_previously_visited_buffer"
meta-= = "/filter toggle"
meta-> = "/input jump_next_visited_buffer"
meta-OA = "/input history_global_previous"
meta-OB = "/input history_global_next"
meta-OC = "/input move_next_word"
meta-OD = "/input move_previous_word"
meta-OF = "/input move_end_of_line"
meta-OH = "/input move_beginning_of_line"
meta-Oa = "/input history_global_previous"
meta-Ob = "/input history_global_next"
meta-Oc = "/input move_next_word"
meta-Od = "/input move_previous_word"
meta2-15~ = "/buffer -1"
meta2-17~ = "/buffer +1"
meta2-18~ = "/window -1"
meta2-19~ = "/window +1"
meta2-1;3A = "/buffer -1"
meta2-1;3B = "/buffer +1"
meta2-1;3C = "/buffer +1"
meta2-1;3D = "/buffer -1"
meta2-1;3F = "/window scroll_bottom"
meta2-1;3H = "/window scroll_top"
meta2-1;5A = "/input history_global_previous"
meta2-1;5B = "/input history_global_next"
meta2-1;5C = "/input move_next_word"
meta2-1;5D = "/input move_previous_word"
meta2-1~ = "/input move_beginning_of_line"
meta2-200~ = "/input paste_start"
meta2-201~ = "/input paste_stop"
meta2-20~ = "/bar scroll title * -30%"
meta2-21~ = "/bar scroll title * +30%"
meta2-23;3~ = "/bar scroll nicklist * b"
meta2-23~ = "/bar scroll nicklist * -100%"
meta2-24;3~ = "/bar scroll nicklist * e"
meta2-24~ = "/bar scroll nicklist * +100%"
meta2-3~ = "/input delete_next_char"
meta2-4~ = "/input move_end_of_line"
meta2-5;3~ = "/window scroll_up"
meta2-5~ = "/window page_up"
meta2-6;3~ = "/window scroll_down"
meta2-6~ = "/window page_down"
meta2-7~ = "/input move_beginning_of_line"
meta2-8~ = "/input move_end_of_line"
meta2-A = "/input history_previous"
meta2-B = "/input history_next"
meta2-C = "/input move_next_char"
meta2-D = "/input move_previous_char"
meta2-F = "/input move_end_of_line"
meta2-G = "/window page_down"
meta2-H = "/input move_beginning_of_line"
meta2-I = "/window page_up"
meta2-Z = "/input complete_previous"
meta2-[E = "/buffer -1"
meta-_ = "/input redo"
meta-a = "/input jump_smart"
meta-b = "/input move_previous_word"
meta-d = "/input delete_next_word"
meta-f = "/input move_next_word"
meta-h = "/input hotlist_clear"
meta-jmeta-f = "/buffer -"
meta-jmeta-l = "/buffer +"
meta-jmeta-r = "/server raw"
meta-jmeta-s = "/server jump"
meta-j01 = "/buffer 1"
meta-j02 = "/buffer 2"
meta-j03 = "/buffer 3"
meta-j04 = "/buffer 4"
meta-j05 = "/buffer 5"
meta-j06 = "/buffer 6"
meta-j07 = "/buffer 7"
meta-j08 = "/buffer 8"
meta-j09 = "/buffer 9"
meta-j10 = "/buffer 10"
meta-j11 = "/buffer 11"
meta-j12 = "/buffer 12"
meta-j13 = "/buffer 13"
meta-j14 = "/buffer 14"
meta-j15 = "/buffer 15"
meta-j16 = "/buffer 16"
meta-j17 = "/buffer 17"
meta-j18 = "/buffer 18"
meta-j19 = "/buffer 19"
meta-j20 = "/buffer 20"
meta-j21 = "/buffer 21"
meta-j22 = "/buffer 22"
meta-j23 = "/buffer 23"
meta-j24 = "/buffer 24"
meta-j25 = "/buffer 25"
meta-j26 = "/buffer 26"
meta-j27 = "/buffer 27"
meta-j28 = "/buffer 28"
meta-j29 = "/buffer 29"
meta-j30 = "/buffer 30"
meta-j31 = "/buffer 31"
meta-j32 = "/buffer 32"
meta-j33 = "/buffer 33"
meta-j34 = "/buffer 34"
meta-j35 = "/buffer 35"
meta-j36 = "/buffer 36"
meta-j37 = "/buffer 37"
meta-j38 = "/buffer 38"
meta-j39 = "/buffer 39"
meta-j40 = "/buffer 40"
meta-j41 = "/buffer 41"
meta-j42 = "/buffer 42"
meta-j43 = "/buffer 43"
meta-j44 = "/buffer 44"
meta-j45 = "/buffer 45"
meta-j46 = "/buffer 46"
meta-j47 = "/buffer 47"
meta-j48 = "/buffer 48"
meta-j49 = "/buffer 49"
meta-j50 = "/buffer 50"
meta-j51 = "/buffer 51"
meta-j52 = "/buffer 52"
meta-j53 = "/buffer 53"
meta-j54 = "/buffer 54"
meta-j55 = "/buffer 55"
meta-j56 = "/buffer 56"
meta-j57 = "/buffer 57"
meta-j58 = "/buffer 58"
meta-j59 = "/buffer 59"
meta-j60 = "/buffer 60"
meta-j61 = "/buffer 61"
meta-j62 = "/buffer 62"
meta-j63 = "/buffer 63"
meta-j64 = "/buffer 64"
meta-j65 = "/buffer 65"
meta-j66 = "/buffer 66"
meta-j67 = "/buffer 67"
meta-j68 = "/buffer 68"
meta-j69 = "/buffer 69"
meta-j70 = "/buffer 70"
meta-j71 = "/buffer 71"
meta-j72 = "/buffer 72"
meta-j73 = "/buffer 73"
meta-j74 = "/buffer 74"
meta-j75 = "/buffer 75"
meta-j76 = "/buffer 76"
meta-j77 = "/buffer 77"
meta-j78 = "/buffer 78"
meta-j79 = "/buffer 79"
meta-j80 = "/buffer 80"
meta-j81 = "/buffer 81"
meta-j82 = "/buffer 82"
meta-j83 = "/buffer 83"
meta-j84 = "/buffer 84"
meta-j85 = "/buffer 85"
meta-j86 = "/buffer 86"
meta-j87 = "/buffer 87"
meta-j88 = "/buffer 88"
meta-j89 = "/buffer 89"
meta-j90 = "/buffer 90"
meta-j91 = "/buffer 91"
meta-j92 = "/buffer 92"
meta-j93 = "/buffer 93"
meta-j94 = "/buffer 94"
meta-j95 = "/buffer 95"
meta-j96 = "/buffer 96"
meta-j97 = "/buffer 97"
meta-j98 = "/buffer 98"
meta-j99 = "/buffer 99"
meta-k = "/input grab_key_command"
meta-l = "/window bare"
meta-m = "/mute mouse toggle"
meta-n = "/window scroll_next_highlight"
meta-p = "/window scroll_previous_highlight"
meta-r = "/input delete_line"
meta-s = "/mute aspell toggle"
meta-u = "/window scroll_unread"
meta-wmeta-meta2-A = "/window up"
meta-wmeta-meta2-B = "/window down"
meta-wmeta-meta2-C = "/window right"
meta-wmeta-meta2-D = "/window left"
meta-wmeta2-1;3A = "/window up"
meta-wmeta2-1;3B = "/window down"
meta-wmeta2-1;3C = "/window right"
meta-wmeta2-1;3D = "/window left"
meta-wmeta-b = "/window balance"
meta-wmeta-s = "/window swap"
meta-x = "/input zoom_merged_buffer"
meta-z = "/window zoom"
ctrl-_ = "/input undo"
[key_search]
ctrl-I = "/input search_switch_where"
ctrl-J = "/input search_stop"
ctrl-M = "/input search_stop"
ctrl-R = "/input search_switch_regex"
meta2-A = "/input search_previous"
meta2-B = "/input search_next"
meta-c = "/input search_switch_case"
[key_cursor]
ctrl-J = "/cursor stop"
ctrl-M = "/cursor stop"
meta-meta2-A = "/cursor move area_up"
meta-meta2-B = "/cursor move area_down"
meta-meta2-C = "/cursor move area_right"
meta-meta2-D = "/cursor move area_left"
meta2-1;3A = "/cursor move area_up"
meta2-1;3B = "/cursor move area_down"
meta2-1;3C = "/cursor move area_right"
meta2-1;3D = "/cursor move area_left"
meta2-A = "/cursor move up"
meta2-B = "/cursor move down"
meta2-C = "/cursor move right"
meta2-D = "/cursor move left"
@item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}"
@item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}"
@item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}"
@item(buffer_nicklist):q = "/window ${_window_number};/query ${nick};/cursor stop"
@item(buffer_nicklist):w = "/window ${_window_number};/whois ${nick}"
@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop"
@chat:m = "hsignal:chat_quote_message;/cursor stop"
@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop"
[key_mouse]
@bar(input):button2 = "/input grab_mouse_area"
@bar(nicklist):button1-gesture-down = "/bar scroll nicklist ${_window_number} +100%"
@bar(nicklist):button1-gesture-down-long = "/bar scroll nicklist ${_window_number} e"
@bar(nicklist):button1-gesture-up = "/bar scroll nicklist ${_window_number} -100%"
@bar(nicklist):button1-gesture-up-long = "/bar scroll nicklist ${_window_number} b"
@chat(script.scripts):button1 = "/window ${_window_number};/script go ${_chat_line_y}"
@chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}"
@chat(script.scripts):wheeldown = "/script down 5"
@chat(script.scripts):wheelup = "/script up 5"
@item(buffer_nicklist):button1 = "/window ${_window_number};/query ${nick}"
@item(buffer_nicklist):button1-gesture-left = "/window ${_window_number};/kick ${nick}"
@item(buffer_nicklist):button1-gesture-left-long = "/window ${_window_number};/kickban ${nick}"
@item(buffer_nicklist):button2 = "/window ${_window_number};/whois ${nick}"
@item(buffer_nicklist):button2-gesture-left = "/window ${_window_number};/ban ${nick}"
@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%"
@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%"
@chat:button1 = "/window ${_window_number}"
@chat:button1-gesture-left = "/window ${_window_number};/buffer -1"
@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1"
@chat:button1-gesture-right = "/window ${_window_number};/buffer +1"
@chat:button1-gesture-right-long = "/window ${_window_number};/input jump_last_buffer"
@chat:ctrl-wheeldown = "/window scroll_horiz -window ${_window_number} +10%"
@chat:ctrl-wheelup = "/window scroll_horiz -window ${_window_number} -10%"
@chat:wheeldown = "/window scroll_down -window ${_window_number}"
@chat:wheelup = "/window scroll_up -window ${_window_number}"
@*:button3 = "/cursor go ${_x},${_y}"

View File

@@ -1,39 +0,0 @@
#
# weechat -- xfer.conf
#
[look]
auto_open_buffer = on
progress_bar_size = 20
pv_tags = "notify_private"
[color]
status_aborted = lightred
status_active = lightblue
status_connecting = yellow
status_done = lightgreen
status_failed = lightred
status_waiting = lightcyan
text = default
text_bg = default
text_selected = white
[network]
blocksize = 65536
fast_send = on
own_ip = ""
port_range = ""
speed_limit = 0
timeout = 300
[file]
auto_accept_chats = off
auto_accept_files = off
auto_accept_nicks = ""
auto_check_crc32 = off
auto_rename = on
auto_resume = on
convert_spaces = on
download_path = "%h/xfer"
upload_path = "~"
use_nick_in_filename = on

View File

@@ -10,7 +10,7 @@ if test -d ${HOME}/.local/bin ; then
fi
if [[ ! -o interactive || ! -t 0 ]]; then
if command -v mise >/dev/null 2>&1 ; then
if [[ "$PWD" != /google* ]] && command -v mise >/dev/null 2>&1 ; then
eval "$(mise activate zsh --shims)"
fi
fi

View File

@@ -1,6 +1,7 @@
# For interactive shells
[[ -n "$ZSH_PROFILE" ]] && {
zshrc_start_time=$(date +%s.%N)
zmodload zsh/datetime
zshrc_start_time=$EPOCHREALTIME
zmodload zsh/zprof
}
HISTFILE=~/.zhistory
@@ -184,6 +185,9 @@ have_command() {
if test -d ${HOME}/.local/bin ; then
export PATH="${HOME}/.local/bin:${PATH}"
fi
if test -d ${HOME}/.npm-packages/bin ; then
export PATH="${HOME}/.npm-packages/bin:${PATH}"
fi
# Source extras and aliases if interactive
if [[ $- == *i* ]] ; then
@@ -191,6 +195,15 @@ if [[ $- == *i* ]] ; then
source_if_existing $HOME/.aliases.local
# zsh-only-ism to avoid error if glob doesn't expand
# specifically sets NULLGLOB for this one glob
typeset -gA _deferred_comps
compdef() {
# Store the arguments: first arg is the function, the rest are the commands
local func=$1
shift
for cmd in "$@"; do
_deferred_comps[$cmd]=$func
done
}
for file in $HOME/.zshrc.d/[a-zA-Z0-9]*.zsh(N) ; do
source "$file"
done
@@ -205,19 +218,27 @@ if [[ $- == *i* ]] ; then
zstyle ':completion:*' users root ${USER}
# Modules after fpath
autoload -Uz compinit
for cmd func in "${(@kv)_deferred_comps}"; do
compdef "$func" "$cmd"
done
unset _deferred_comps
# Regenerate zcompdump if it's older than any file in fpath
DUMPFILE="${ZDOTDIR:-$HOME}/.zcompdump"
updated_files=(${^fpath}(N.om[1]))
# Find the newest file across all fpath directories to detect edits/additions
# (N.om[1]) = Null-glob, plain files only, sort by mtime, pick the first (newest)
local newest_comp=(${^fpath}/*(N.om[1]))
if [[ ! -f "$DUMPFILE" || ( ${#updated_files} -gt 0 && "$updated_files[1]" -nt "$DUMPFILE" ) ]]; then
compinit -i -D "$DUMPFILE"
# Asynchronously compile the dump file
{ zcompile "$DUMPFILE" } &!
# If any completion file was modified, or dump doesn't exist, we might need to update.
if [[ ! -f "$DUMPFILE" || ( -n "$newest_comp" && "$newest_comp" -nt "$DUMPFILE" ) ]]; then
compinit -i -d "$DUMPFILE"
# Asynchronously compile the dump file with an atomic rename
{ zcompile "$DUMPFILE.zwc.tmp" "$DUMPFILE" && mv -f "$DUMPFILE.zwc.tmp" "$DUMPFILE.zwc" } &!
else
compinit -C -i -D "$DUMPFILE"
compinit -C -i -d "$DUMPFILE"
fi
unset DUMPFILE updated_files
unset DUMPFILE newest_comp
autoload -Uz promptinit && promptinit
# Virtualenvwrapper
source_first_existing \
@@ -267,8 +288,13 @@ if [ -x /usr/bin/ack-grep ] ; then
alias ack='/usr/bin/ack-grep'
fi
# I want this first always
# I want these first always
PATH="${HOME}/bin:${PATH}"
if [[ "$(uname)" == "Darwin" ]]; then
PATH="${HOME}/bin/macos:${PATH}"
elif [[ "$(uname)" == "Linux" ]]; then
PATH="${HOME}/bin/linux:${PATH}"
fi
# Load any local settings
source_if_existing $HOME/.zshrc.local
@@ -292,9 +318,9 @@ fi
typeset -U PATH
if [[ -n "$ZSH_PROFILE" ]]; then
zshrc_end_time=$(date +%s.%N)
elapsed_seconds=$(echo "$zshrc_end_time - $zshrc_start_time" | bc -l)
elapsed_ms=$(printf "%.0f" "$(echo "$elapsed_seconds * 1000" | bc -l)")
echo "zshrc done: ${elapsed_ms}ms"
zshrc_end_time=$EPOCHREALTIME
# Calculation in ms using zsh floating point math
elapsed_ms=$(( (zshrc_end_time - zshrc_start_time) * 1000 ))
printf "zshrc done: %.0fms\n" "$elapsed_ms"
zprof
fi

View File

@@ -18,8 +18,15 @@ alert() {
icon="error"
fi
if [ "$(uname)" = "Darwin" ]; then
# macOS notification
local title="Finished: '$*'"
local msg="Exit code: $ret"
osascript -e "display notification \"$msg\" with title \"$title\""
else
# Send the notification with the executed command
notify-send --urgency=low -i "$icon" "Finished: '$@'"
fi
# Return the original exit code
return $ret

View File

@@ -1,7 +1,12 @@
ANDROID_HOME=$HOME/Library/Android/sdk
if [ "$(uname)" = "Darwin" ]; then
ANDROID_HOME=$HOME/Library/Android/sdk
else
ANDROID_HOME=$HOME/Android/Sdk
fi
if test -d $ANDROID_HOME ; then
PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools
if test -d "${ANDROID_HOME}" ; then
export ANDROID_HOME
PATH="${PATH}:${ANDROID_HOME}/emulator:${ANDROID_HOME}/platform-tools"
else
unset ANDROID_HOME
fi

View File

@@ -6,7 +6,11 @@ if have_command nasm && have_command objdump ; then
local TMPF=`mktemp`
local bytes
local byte
$NASM -f elf -o $TMPF $1
local format="elf"
if [[ "$OSTYPE" == darwin* ]]; then
format="macho64"
fi
$NASM -f $format -o $TMPF $1
$OBJDUMP -M intel -d $TMPF | grep '^ ' | cut -f2 | while read -A bytes ; do
for byte in $bytes ; do
echo -n "\\\\x$byte"

View File

@@ -1,5 +1,11 @@
function dumpenv {
if [ "$(uname)" = "Linux" ]; then
tr '\0' '\n' < /proc/${1}/environ
elif [ "$(uname)" = "Darwin" ]; then
# macOS doesn't have /proc, use ps instead.
# Note: this may truncate if environment is very large.
ps -p ${1} -wwwe -o command= | tr ' ' '\n' | grep '='
fi
}
if test -x "/sbin/starship" ; then

View File

@@ -7,7 +7,7 @@ export GEMINI_CONTEXT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/gemini"
mkdir -p "$GEMINI_CONTEXT_DIR"
# Template settings file
GEMINI_TEMPLATE_SETTINGS="${HOME}/.skel/dotfiles/config/gemini/settings.json"
GEMINI_TEMPLATE_SETTINGS="${GEMINI_CONTEXT_DIR}/settings.json"
# Create a new Gemini context
gemini-context-create() {
@@ -19,8 +19,13 @@ gemini-context-create() {
local context_name="$1"
local context_path="$GEMINI_CONTEXT_DIR/$context_name"
if [[ "$context_name" =~ [/] ]]; then
echo "Error: Context name cannot contain slashes."
if [[ "$context_name" =~ [^a-zA-Z0-9_-] ]]; then
echo "Error: Context name should only contain alphanumeric characters, underscores, or dashes."
return 1
fi
if [[ "$context_name" == .* ]]; then
echo "Error: Context name cannot start with a dot."
return 1
fi
@@ -42,25 +47,90 @@ gemini-context-create() {
echo "Context '$context_name' created."
}
# List available Gemini contexts
# List available Gemini contexts (directories only, containing .gemini)
_gemini_context_list_internal() {
command ls "$GEMINI_CONTEXT_DIR" 2>/dev/null
local contexts
# Use Zsh globbing: find directories containing a .gemini subdir
contexts=($GEMINI_CONTEXT_DIR/*/.gemini(N/:h:t))
[[ ${#contexts} -gt 0 ]] && print -l "${contexts[@]}"
}
gemini-context-list() {
echo "Available Gemini contexts:"
local contexts=$(_gemini_context_list_internal)
if [ -z "$contexts" ]; then
local contexts=($(_gemini_context_list_internal))
if [ ${#contexts} -eq 0 ]; then
echo " (No contexts found)"
return
fi
for context in $contexts; do
if [ -d "$GEMINI_CONTEXT_DIR/$context" ]; then
for context in "${contexts[@]}"; do
if [ "$GEMINI_CLI_HOME" = "$GEMINI_CONTEXT_DIR/$context" ]; then
echo " * $context (active)"
else
echo " $context"
fi
done
}
# Delete a Gemini context
gemini-context-delete() {
local context_name
if [ -z "$1" ]; then
if command -v fzf >/dev/null; then
context_name=$(_gemini_context_list_internal | fzf --prompt="Select Gemini Context to DELETE: ")
[[ -z "$context_name" ]] && return 1
else
echo "Usage: gemini-context-delete <name>"
return 1
fi
else
context_name="$1"
fi
local context_path="$GEMINI_CONTEXT_DIR/$context_name"
if [ ! -d "$context_path" ]; then
echo "Error: Context '$context_name' not found."
return 1
fi
if read -q "confirm?Are you sure you want to delete context '$context_name'? [y/N] "; then
echo
rm -rf "$context_path"
echo "Context '$context_name' deleted."
if [ "$GEMINI_CLI_HOME" = "$context_path" ]; then
gemini-context-unset
fi
else
echo "\nDeletion cancelled."
fi
}
# Rename a Gemini context
gemini-context-rename() {
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: gemini-context-rename <old_name> <new_name>"
return 1
fi
local old_path="$GEMINI_CONTEXT_DIR/$1"
local new_path="$GEMINI_CONTEXT_DIR/$2"
if [ ! -d "$old_path" ]; then
echo "Error: Source context '$1' not found."
return 1
fi
if [ -d "$new_path" ]; then
echo "Error: Destination context '$2' already exists."
return 1
fi
mv "$old_path" "$new_path"
echo "Context '$1' renamed to '$2'."
if [ "$GEMINI_CLI_HOME" = "$old_path" ]; then
gemini-context-use -q "$2"
fi
}
# Use a specific Gemini context
gemini-context-use() {
local context_name
@@ -89,14 +159,13 @@ gemini-context-use() {
local context_path="$GEMINI_CONTEXT_DIR/$context_name"
if [ ! -d "$context_path" ]; then
[[ "$quiet" -eq 0 ]] && echo "Error: Context '$context_name' not found at $context_path"
[[ "$quiet" -eq 0 ]] && gemini-context-list
if [ ! -d "$context_path/.gemini" ]; then
[[ "$quiet" -eq 0 ]] && echo "Error: '$context_name' is not a valid Gemini context (missing .gemini directory)."
return 1
fi
export GEMINI_CLI_HOME="$context_path"
[[ "$quiet" -eq 0 ]] && echo "Switched to Gemini context '$context_name' (GEMINI_CLI_HOME=$GEMINI_CLI_HOME)"
[[ "$quiet" -eq 0 ]] && echo "Switched to Gemini context '$context_name'"
}
# Edit a context's settings
@@ -136,13 +205,12 @@ gemini-context-edit() {
# Show the current Gemini context
gemini-context-current() {
if [ -n "$GEMINI_CLI_HOME" ]; then
local current_context=$(basename "$GEMINI_CLI_HOME")
local current_context=${GEMINI_CLI_HOME:t}
if [ "$GEMINI_CONTEXT_DIR/$current_context" = "$GEMINI_CLI_HOME" ]; then
echo "Current Gemini context: $current_context"
else
echo "Current Gemini context: Custom"
echo "Current Gemini context: Custom ($GEMINI_CLI_HOME)"
fi
echo "GEMINI_CLI_HOME=$GEMINI_CLI_HOME"
else
echo "No Gemini context set, using default."
fi
@@ -157,12 +225,12 @@ gemini-context-unset() {
# Alias for gemini-context-use
alias gemctx='gemini-context-use'
# Zsh Completion for gemini-context-use and gemctx
# Zsh Completion
_gemini_contexts() {
local contexts
local -a contexts
contexts=($(_gemini_context_list_internal))
_describe 'gemini contexts' contexts
}
compdef _gemini_contexts gemini-context-use
compdef _gemini_contexts gemctx
compdef _gemini_contexts gemini-context-edit
compdef _gemini_contexts gemini-context-use gemctx gemini-context-edit gemini-context-delete gemini-context-rename

View File

@@ -1,8 +1,20 @@
test -f /usr/share/source-highlight/src-hilite-lesspipe.sh && \
# Find src-hilite-lesspipe.sh
_SRCHILITE=""
for _p in /usr/share/source-highlight/src-hilite-lesspipe.sh /opt/homebrew/bin/src-hilite-lesspipe.sh /usr/local/bin/src-hilite-lesspipe.sh ; do
if [ -f "$_p" ] ; then
_SRCHILITE="$_p"
break
fi
done
if [ -n "$_SRCHILITE" ] ; then
function srcless {
if [ $# -ne 1 ] ; then
echo "$0 <what>" > /dev/stderr
echo "Usage: srcless <file>" > /dev/stderr
return 1
fi
/usr/share/source-highlight/src-hilite-lesspipe.sh $1 | less -R
"$_SRCHILITE" "$1" | less -R
}
fi
unset _SRCHILITE _p

View File

@@ -3,6 +3,17 @@
# Skelify -- move a file to my .skel and setup symlinks
function skelify {
local -A opts
zparseopts -D -A opts -overlay:
local overlay_name="${opts[--overlay]}"
local base_skel_dir="${HOME}/.skel/dotfiles"
local extra_args=()
if [[ -n "${overlay_name}" ]]; then
base_skel_dir="${HOME}/.skel/dotfile_overlays/${overlay_name}"
extra_args=(--overlay "${overlay_name}")
fi
local target
local whichdir
local relhome
@@ -10,16 +21,19 @@ function skelify {
local fulltarget
for target in $~@; do
if test -d ${target} ; then
skelify ${target}/* || return 1
skelify "${extra_args[@]}" ${target}/* || return 1
elif test -f ${target} ; then
if ! whichdir=$(cd $(dirname $target) && pwd); then
echo Could not find directory for $target >/dev/stderr
return 1
fi
fname=$(basename ${target})
relhome=${whichdir#${HOME}/}
fulltarget="${whichdir}/${fname}"
if [[ ${relhome} == ${whichdir} ]] ; then
if [[ ${whichdir} == ${HOME} ]] ; then
relhome=""
elif [[ ${whichdir} == ${HOME}/* ]] ; then
relhome=${whichdir#${HOME}/}
else
echo ${whichdir} is not in home >/dev/stderr
return 1
fi
@@ -32,7 +46,7 @@ function skelify {
return 1
fi
echo ${target}
local skeldir="${HOME}/.skel/dotfiles/${relhome}"
local skeldir="${base_skel_dir}/${relhome}"
mkdir -p "${skeldir}"
mv ${target} "${skeldir}/${fname}"
ln -s "${skeldir}/${fname}" "${fulltarget}"

56
dotfiles/zshrc.d/util.zsh Normal file
View File

@@ -0,0 +1,56 @@
# utility function to "open" a file
o() {
if [[ "$OSTYPE" == "darwin"* ]]; then
open "$@"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
xdg-open "$@"
else
echo "Unknown OS"
fi
}
# Copy from stdin to the system clipboard
syscopy() {
if command -v pbcopy >/dev/null 2>&1; then
# macOS
pbcopy "$@"
elif command -v wl-copy >/dev/null 2>&1; then
# Linux Wayland
wl-copy "$@"
elif command -v xclip >/dev/null 2>&1; then
# Linux X11
xclip -selection clipboard "$@"
elif command -v xsel >/dev/null 2>&1; then
# Linux X11 (alternative)
xsel --clipboard --input "$@"
elif command -v clip.exe >/dev/null 2>&1; then
# Windows WSL
clip.exe "$@"
else
echo "Error: No clipboard utility found. Please install pbcopy, wl-copy, xclip, or xsel." >&2
return 1
fi
}
# Paste from the system clipboard to stdout
syspaste() {
if command -v pbpaste >/dev/null 2>&1; then
# macOS
pbpaste "$@"
elif command -v wl-paste >/dev/null 2>&1; then
# Linux Wayland
wl-paste "$@"
elif command -v xclip >/dev/null 2>&1; then
# Linux X11
xclip -selection clipboard -o "$@"
elif command -v xsel >/dev/null 2>&1; then
# Linux X11 (alternative)
xsel --clipboard --output "$@"
elif command -v powershell.exe >/dev/null 2>&1; then
# Windows WSL
powershell.exe -noprofile -command Get-Clipboard "$@"
else
echo "Error: No clipboard utility found. Please install pbpaste, wl-paste, xclip, or xsel." >&2
return 1
fi
}

View File

@@ -229,7 +229,7 @@ install_main() {
# macOS specific Homebrew bundle installation
if [[ "$(uname)" == "Darwin" ]] && have_command brew && [[ -f "${BASEDIR}/Brewfile" ]]; then
verbose "Checking Homebrew bundle..."
brew bundle install --file="${BASEDIR}/Brewfile" --no-lock
brew bundle install --file="${BASEDIR}/Brewfile"
fi
[[ "$INSTALL_KEYS" = 1 ]] && install_keys

View File

@@ -1,9 +1,12 @@
anthropic.claude-code
bazelbuild.vscode-bazel
dnut.rewrap-revived
docker.docker
formulahendry.code-runner
github.vscode-codeql
golang.go
hverlin.mise-vscode
kilocode.Kilo-Code
ms-azuretools.vscode-containers
ms-azuretools.vscode-docker
ms-python.black-formatter