From 8495cbaa240b506e33643d0417cd63b2f7999918 Mon Sep 17 00:00:00 2001 From: rootiest Date: Sat, 4 Jul 2026 02:24:37 -0400 Subject: [PATCH 1/2] feat(config): auto-manage user-dots convenience symlink Point $__fish_config_dir/user-dots at $__fish_user_dots_path on interactive startup so the private overlay can be browsed from the fish config dir. The link is created if missing and repointed if the path variable changes, only ever managing a symlink (never clobbering a real file/dir). Gated as a C2 startup side-effect (__fish_config_op_autoexec) and git-ignored. Docs: README overlay section and fish-config.md C2 table updated. --- .gitignore | 1 + README.md | 2 ++ config.fish | 16 ++++++++++++++++ docs/fish-config.md | 8 ++++++-- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e77cadb..c086b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -299,3 +299,4 @@ AGENTS/ docs/devlogs # ──────────────────────────────────────────────────────── /.cache_ggshield +user-dots diff --git a/README.md b/README.md index a73e2e2..beaa023 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,8 @@ test -f "$__fish_user_dots_path/local.fish" and source "$__fish_user_dots_path/local.fish" ``` +For convenience, a git-ignored `user-dots` symlink in the fish config directory is pointed at `$__fish_user_dots_path` on startup, so the overlay can be browsed from `~/.config/fish/`. It is recreated if missing and repointed if the path variable changes. This is a C2 startup side-effect (`__fish_config_op_autoexec`); it only ever manages a symlink and never clobbers a real file or directory at that path. + `fish_variables` (which fish auto-manages and may contain universal variable state) is excluded from this repo via `.gitignore`. --- diff --git a/config.fish b/config.fish index 058e85c..e260113 100644 --- a/config.fish +++ b/config.fish @@ -226,6 +226,22 @@ if status is-interactive # Resolve user-dots path. Customize via: set -U __fish_user_dots_path /your/path set -q __fish_user_dots_path or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish" + # ────────────────────── user-dots convenience symlink ─────────────────── + # Keep $__fish_config_dir/user-dots pointing at the resolved path so it can + # be browsed from the fish config dir. Git-ignored; a C2 startup side-effect. + # Only ever manages a symlink — never clobbers a real file/dir placed there. + if __fish_config_op_enabled __fish_config_op_autoexec + set -l __udots_link "$__fish_config_dir/user-dots" + if test -d "$__fish_user_dots_path" + if test -L "$__udots_link" + test (readlink "$__udots_link") != "$__fish_user_dots_path" + and ln -sfn "$__fish_user_dots_path" "$__udots_link" + else if not test -e "$__udots_link" + ln -s "$__fish_user_dots_path" "$__udots_link" + end + end + end + # ─────────────────────── Source machine-local config ──────────────────── # Sources local.fish if it exists. That file handles sourcing its own # secrets.fish companion when needed. diff --git a/docs/fish-config.md b/docs/fish-config.md index 16619aa..264657a 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1823,10 +1823,14 @@ __fish_config_op_autoexec prevents all of them. Python venv activation On every cd Sources .venv/bin/activate.fish WakaTime command hook On every command Reports to WakaTime API Auto-pull fast-forward On entering a repo Background ff-only git pull + user-dots symlink Every startup Links $__fish_config_dir/user-dots + to $__fish_user_dots_path When C2 is disabled: no Fisher install, no theme application, no paru/yay -wrapper generation, no automatic venv activation, no WakaTime reporting, and -no auto-pull (the PWD handler is never registered). +wrapper generation, no automatic venv activation, no WakaTime reporting, +no auto-pull (the PWD handler is never registered), and the user-dots +convenience symlink is not created. The symlink is git-ignored and only ever +managed as a symlink — a real file or directory at that path is left untouched. The first-run completion marker (__fish_config_first_run_complete) is still set so the init does not re-run on subsequent shells. From ec47b0671c00409dd6f05e8f19b461101eeb8037 Mon Sep 17 00:00:00 2001 From: rootiest Date: Sat, 4 Jul 2026 02:34:20 -0400 Subject: [PATCH 2/2] feat(config-settings): add Dots link toggle for user-dots symlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the user-dots symlink logic into the __fish_user_dots_link helper and add a dedicated __fish_user_dots_symlink toggle, surfaced as a 'Dots link' bool row on the config-settings Paths page. Unlike the opinionated categories it has no universal/session split — it acts on a literal path. Setting it falsy (or toggling off) stops symlink generation and removes any existing link immediately, honoured regardless of the C2 master switch. Creation remains a C2 startup side-effect. config.fish now calls the helper. Docs: README, fish-config.md (C2 table, Paths page, machine-local section), and fish-config.index updated. --- README.md | 4 +- config.fish | 19 ++------ docs/fish-config.index | 2 + docs/fish-config.md | 17 ++++++- functions/__config_settings_draw_value.fish | 8 ++-- functions/__fish_user_dots_link.fish | 53 +++++++++++++++++++++ functions/config-settings.fish | 31 ++++++++---- 7 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 functions/__fish_user_dots_link.fish diff --git a/README.md b/README.md index beaa023..9d50965 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,9 @@ test -f "$__fish_user_dots_path/local.fish" and source "$__fish_user_dots_path/local.fish" ``` -For convenience, a git-ignored `user-dots` symlink in the fish config directory is pointed at `$__fish_user_dots_path` on startup, so the overlay can be browsed from `~/.config/fish/`. It is recreated if missing and repointed if the path variable changes. This is a C2 startup side-effect (`__fish_config_op_autoexec`); it only ever manages a symlink and never clobbers a real file or directory at that path. +For convenience, a git-ignored `user-dots` symlink in the fish config directory is pointed at `$__fish_user_dots_path` on startup, so the overlay can be browsed from `~/.config/fish/`. It is recreated if missing and repointed if the path variable changes. Creation is a C2 startup side-effect (`__fish_config_op_autoexec`); it only ever manages a symlink and never clobbers a real file or directory at that path. + +To opt out, set `__fish_user_dots_symlink` to a falsy value (or toggle **Dots link** off on the **Paths** page of `config-settings`). Disabling it stops the symlink being generated and removes any existing one immediately — honoured regardless of the C2 master switch. `fish_variables` (which fish auto-manages and may contain universal variable state) is excluded from this repo via `.gitignore`. diff --git a/config.fish b/config.fish index e260113..5561769 100644 --- a/config.fish +++ b/config.fish @@ -227,20 +227,11 @@ if status is-interactive set -q __fish_user_dots_path or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish" # ────────────────────── user-dots convenience symlink ─────────────────── - # Keep $__fish_config_dir/user-dots pointing at the resolved path so it can - # be browsed from the fish config dir. Git-ignored; a C2 startup side-effect. - # Only ever manages a symlink — never clobbers a real file/dir placed there. - if __fish_config_op_enabled __fish_config_op_autoexec - set -l __udots_link "$__fish_config_dir/user-dots" - if test -d "$__fish_user_dots_path" - if test -L "$__udots_link" - test (readlink "$__udots_link") != "$__fish_user_dots_path" - and ln -sfn "$__fish_user_dots_path" "$__udots_link" - else if not test -e "$__udots_link" - ln -s "$__fish_user_dots_path" "$__udots_link" - end - end - end + # Keep $__fish_config_dir/user-dots tracking the resolved path so it can be + # browsed from the fish config dir. Git-ignored. Controlled by the + # __fish_user_dots_symlink toggle (config-settings → Paths); creation is a + # C2 side-effect, removal-on-opt-out is honoured regardless. See the helper. + __fish_user_dots_link # ─────────────────────── Source machine-local config ──────────────────── # Sources local.fish if it exists. That file handles sourcing its own diff --git a/docs/fish-config.index b/docs/fish-config.index index 9fc1e8e..f47089b 100644 --- a/docs/fish-config.index +++ b/docs/fish-config.index @@ -216,6 +216,8 @@ install-methods=## Install Methods customization=# 7. CUSTOMIZATION customize=# 7. CUSTOMIZATION local=## Machine-local Configuration +user-dots=## Machine-local Configuration +dots-link=## Machine-local Configuration secrets=## Secrets and API Keys overrides=## Overriding Configuration Variables universal=## Fish Universal Variables diff --git a/docs/fish-config.md b/docs/fish-config.md index 264657a..00b7e06 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1435,8 +1435,9 @@ Add -i (interactive confirmation) to destructive commands: Sponge — sponge history-scrubbing settings: delay, successful exit codes, purge-only-on-exit, allow-previously-successful, and extra sensitive variable-name tokens - Paths — scrollback log directory, scrollback max files, and the - user-dots path + Paths — scrollback log directory, scrollback max files, the + user-dots path, and the user-dots convenience symlink toggle + (Dots link) Toggle rows use ← → (or h/l) along an OFF ← DEFAULT → ON scale; DEFAULT erases the variable so the master switch / built-in default applies. Value @@ -1666,6 +1667,14 @@ custom location with: Typical uses: additional PATH entries, local aliases, hostname-specific env vars, work-specific tool configs. +For convenience, a git-ignored `user-dots` symlink in the fish config +directory tracks `$__fish_user_dots_path` so the overlay can be browsed from +`~/.config/fish/`. It is created if missing and repointed if the path changes. +Opt out by setting `__fish_user_dots_symlink` to a falsy value, or toggling +"Dots link" off on the config-settings Paths page — this stops generation and +removes any existing link. It only ever manages a symlink and never clobbers a +real file or directory at that path. + ## Secrets and API Keys $__fish_user_dots_path/secrets.fish @@ -1831,6 +1840,10 @@ wrapper generation, no automatic venv activation, no WakaTime reporting, no auto-pull (the PWD handler is never registered), and the user-dots convenience symlink is not created. The symlink is git-ignored and only ever managed as a symlink — a real file or directory at that path is left untouched. +The symlink has its own opt-out independent of C2: set __fish_user_dots_symlink +to a falsy value (or toggle "Dots link" off on the config-settings Paths page) +to stop generating it and remove any existing link — honoured even when C2 is +enabled. Managed by the __fish_user_dots_link helper. The first-run completion marker (__fish_config_first_run_complete) is still set so the init does not re-run on subsequent shells. diff --git a/functions/__config_settings_draw_value.fish b/functions/__config_settings_draw_value.fish index 27e015d..5b2b505 100644 --- a/functions/__config_settings_draw_value.fish +++ b/functions/__config_settings_draw_value.fish @@ -57,10 +57,10 @@ function __config_settings_draw_value else set title "Path Settings" set active_idx 3 - set vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path - set labels "Log dir" "Log max" "Dots path" - set types path int path - set hints "~/.terminal_history" 100 "(default)" + set vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path __fish_user_dots_symlink + set labels "Log dir" "Log max" "Dots path" "Dots link" + set types path int path bool + set hints "~/.terminal_history" 100 "(default)" on end set -l nrows (count $vars) diff --git a/functions/__fish_user_dots_link.fish b/functions/__fish_user_dots_link.fish new file mode 100644 index 0000000..6873671 --- /dev/null +++ b/functions/__fish_user_dots_link.fish @@ -0,0 +1,53 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __fish_user_dots_link +# +# DESCRIPTION +# Manages the git-ignored `user-dots` convenience symlink in the fish config +# directory ($__fish_config_dir/user-dots), pointing it at the resolved +# $__fish_user_dots_path so the private overlay can be browsed from +# ~/.config/fish/. +# +# Behaviour is controlled by the __fish_user_dots_symlink toggle: +# - explicit falsy (0/false/…) — remove our symlink if present and stop. +# Honoured regardless of C2, so opting out always cleans up. +# - unset or truthy (default) — create the link if missing and repoint it +# if the target changed. This is a C2 startup side-effect, so it only +# creates when __fish_config_op_autoexec is enabled. +# +# Only ever manages a symlink; a real file or directory at that path is left +# untouched. +# +# ARGUMENTS +# (none) +# +# RETURNS +# 0 Always +# +# EXAMPLE +# __fish_user_dots_link +function __fish_user_dots_link --description 'Manage the user-dots convenience symlink' + set -l link "$__fish_config_dir/user-dots" + set -q __fish_user_dots_path + or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish" + + # Explicit opt-out: remove our symlink (never a real file/dir) and stop. + __fish_variable_check __fish_user_dots_symlink + if test $status -eq 1 + test -L "$link"; and rm -f "$link" + return 0 + end + + # Enabled: creation is a C2 startup side-effect. + __fish_config_op_enabled __fish_config_op_autoexec; or return 0 + + test -d "$__fish_user_dots_path"; or return 0 + if test -L "$link" + test (readlink "$link") != "$__fish_user_dots_path" + and ln -sfn "$__fish_user_dots_path" "$link" + else if not test -e "$link" + ln -s "$__fish_user_dots_path" "$link" + end +end diff --git a/functions/config-settings.fish b/functions/config-settings.fish index 6e76229..9e7ec13 100644 --- a/functions/config-settings.fish +++ b/functions/config-settings.fish @@ -80,9 +80,9 @@ function config-settings --description 'Interactive TUI for managing fish config set -l sponge_vars sponge_delay sponge_purge_only_on_exit sponge_allow_previously_successful sponge_successful_exit_codes __fish_sponge_extra_sensitive set -l sponge_types int bool bool list list set -l sponge_labels Delay "Purge@exit" "Allow prev" "OK codes" "Extra secret" - set -l paths_vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path - set -l paths_types path int path - set -l paths_labels "Log dir" "Log max" "Dots path" + set -l paths_vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path __fish_user_dots_symlink + set -l paths_types path int path bool + set -l paths_labels "Log dir" "Log max" "Dots path" "Dots link" # Reset/blank-edit target for each value row. A non-empty entry is written # verbatim (sponge reads sponge_delay / sponge_successful_exit_codes with no @@ -91,10 +91,10 @@ function config-settings --description 'Interactive TUI for managing fish config # extra-sensitive list all tolerate being unset). Bool rows are not reset # through this path — they are a 2-state true/false with no unset state. set -l sponge_defaults 2 '' '' 0 '' - set -l paths_defaults '' '' '' + set -l paths_defaults '' '' '' '' # Rows per page index 0..3 - set -l page_rows 7 7 5 3 + set -l page_rows 7 7 5 4 set -l cur_page 0 # 0=Universal 1=Session 2=Sponge 3=Paths set -l cur_row 0 @@ -162,12 +162,21 @@ function config-settings --description 'Interactive TUI for managing fish config set -l next_val on test "$cur_val" = off; and set next_val DEFAULT __config_settings_apply $varname $scope $next_val - else if test $cur_page -eq 2 - # Sponge bool rows are 2-state (true/false) with no unset - # state — sponge reads them with no fallback. → sets true. + else + # Value pages: bool rows are 2-state (true/false). → sets + # true. Sponge reads its bools with no fallback; the Paths + # "Dots link" bool drives __fish_user_dots_link on change. + set -l v_vars $sponge_vars + set -l v_types $sponge_types + if test $cur_page -eq 3 + set v_vars $paths_vars + set v_types $paths_types + end set -l ridx (math $cur_row + 1) - if test "$sponge_types[$ridx]" = bool - set -U $sponge_vars[$ridx] true 2>/dev/null + if test "$v_types[$ridx]" = bool + set -U $v_vars[$ridx] true 2>/dev/null + test "$v_vars[$ridx]" = __fish_user_dots_symlink + and __fish_user_dots_link end end case left h @@ -196,6 +205,8 @@ function config-settings --description 'Interactive TUI for managing fish config set -l vtype $v_types[$ridx] if test "$vtype" = bool set -U $varname false 2>/dev/null + test "$varname" = __fish_user_dots_symlink + and __fish_user_dots_link else __config_settings_set_value $varname $vtype "$v_defaults[$ridx]" end