From 8e7e450a9d801d32002f3d4cb73449715db6403f Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 21:51:27 -0400 Subject: [PATCH 1/7] docs: clarify that plugin functionality is bundled, not managed by fisher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only jorgebucaran/fisher itself is in fish_plugins. The functionality from catppuccin/fish, fzf.fish, done, autopair, and puffer-fish is bundled directly with this config in modified form and must not be installed or updated via fisher. Removes sponge (not present), corrects file path mappings for bundled components, fixes stale "Plugin sync failed" message in first_run.fish, and expands fish-config.index with missing sections 9–11. --- README.md | 4 ++-- conf.d/first_run.fish | 2 +- docs/fish-config.index | 24 +++++++++++++++++----- docs/fish-config.md | 46 ++++++++++++++++++++++++++++-------------- requirements.md | 8 ++++++-- 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8b8155f..ccb7662 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This config layers on top of the CachyOS base Fish configuration and adds: - **Catppuccin Mocha** theming throughout (prompt, FZF, syntax highlighting) - **Starship** prompt with VI key bindings -- **Fisher** plugin management with FZF key bindings and Catppuccin syntax colors +- **Fisher** plugin manager bootstrapped automatically; plugin functionality (FZF bindings, Catppuccin theme, done notifications, autopair, puffer-fish) bundled directly with the config - **Smart CLI wrappers** that prefer modern tools (`eza`, `bat`, `btop`, `dust`, `prettyping`) with graceful fallbacks - **Auto Python venv** activation on directory change (direnv-aware) - **Kitty terminal** deep integration for splits, tabs, and SSH @@ -66,7 +66,7 @@ mv ~/.config/fish ~/.config/fish.bak git clone https://git.rootiest.dev/rootiest/fish-config.git ~/.config/fish ``` -Then open a new Fish shell — Fisher and all plugins will be installed automatically on first launch, and the Catppuccin Mocha theme will be applied. +Then open a new Fish shell — Fisher will be installed automatically on first launch and the Catppuccin Mocha theme will be applied. All plugin functionality is bundled directly with this config and requires no additional installation. A [chezmoi](https://www.chezmoi.io/) dotfile manager is also configured — secrets are sourced from `~/.config/.user-dots/fish/secrets.fish` and excluded from version control. diff --git a/conf.d/first_run.fish b/conf.d/first_run.fish index b6f6e48..ef96983 100644 --- a/conf.d/first_run.fish +++ b/conf.d/first_run.fish @@ -35,7 +35,7 @@ if not type -q fisher if curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source echo " [first-run] Fisher installed." if not fisher update 2>/dev/null - echo " [first-run] Plugin sync failed — run 'fisher update' manually." >&2 + echo " [first-run] Fisher update failed — run 'fisher update' manually." >&2 end else echo " [first-run] Fisher install failed — run 'fisher update' manually." >&2 diff --git a/docs/fish-config.index b/docs/fish-config.index index 74e80b1..273c354 100644 --- a/docs/fish-config.index +++ b/docs/fish-config.index @@ -29,8 +29,8 @@ keybindings=# 3. KEY BINDINGS bindings=# 3. KEY BINDINGS key-bindings=# 3. KEY BINDINGS keys=# 3. KEY BINDINGS -fzf-bindings=## FZF Bindings (from fzf --fish integration) -fzf=## FZF Bindings (from fzf --fish integration) +fzf-bindings=## FZF Bindings (bundled from PatrickF1/fzf.fish) +fzf=## FZF Bindings (bundled from PatrickF1/fzf.fish) # ── Section 4: Abbreviations ────────────────────────────────── abbreviations=# 4. ABBREVIATIONS @@ -166,10 +166,24 @@ universal=## Fish Universal Variables # ── Section 8: Fisher Plugins ───────────────────────────────── plugins=# 8. FISHER PLUGINS fisher=# 8. FISHER PLUGINS +bundled-plugins=## Bundled Plugin Functionality +fish-plugins-manifest=## fish_plugins Manifest -# ── Section 9: Viewing This Manual ─────────────────────────── -viewing=# 9. VIEWING THIS MANUAL -manual=# 9. VIEWING THIS MANUAL +# ── Section 9: Installation ──────────────────────────────────── +installation=# 9. INSTALLATION +install=# 9. INSTALLATION +sentinel=## Return Sentinel +updating=## Updating + +# ── Section 10: Personalization ─────────────────────────────── +personalization=# 10. PERSONALIZATION +personalize=# 10. PERSONALIZATION +secrets=## secrets.fish +local-config=## local.fish + +# ── Section 11: Viewing This Manual ────────────────────────── +viewing=# 11. VIEWING THIS MANUAL +manual=# 11. VIEWING THIS MANUAL ov=## With ov (recommended) man-page=## As a man page (if compiled) manpage=## As a man page (if compiled) diff --git a/docs/fish-config.md b/docs/fish-config.md index af8c2fb..57c6dcc 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -279,7 +279,7 @@ are active in Insert, Normal, and Visual modes unless noted. line to open an fzf picker and insert a selection at the cursor position. -## FZF Bindings (from PatrickF1/fzf.fish) +## FZF Bindings (bundled from PatrickF1/fzf.fish) Ctrl+R Search command history Ctrl+Alt+F Search git-tracked files @@ -1395,9 +1395,9 @@ To customize, override FZF_DEFAULT_OPTS in local.fish. ### Catppuccin Mocha Syntax Highlighting -The catppuccin/fish plugin applies syntax highlight colors on first run. -Colors are stored in fish_variables (universal). To switch variants, install -a different theme from the themes/ directory: +The Catppuccin Mocha theme ships with this config in themes/ and is applied +on first run via `conf.d/first_run.fish`. Colors are stored in fish_variables +(universal). To switch variants, install a different theme from themes/: fish_config theme save "Catppuccin Latte" @@ -1417,18 +1417,33 @@ testing), run: Then open a new shell. -The plugin list is maintained in fish_plugins at the config root. +## Bundled Plugin Functionality + +Plugin functionality is bundled directly with this config rather than managed +through Fisher. The bundled versions include customizations for Fish 4.x +compatibility and improved behavior that differ from their upstream releases. +Installing them through Fisher would overwrite these customizations. + +Bundled components and their upstream origins: + + catppuccin/fish → themes/ + conf.d/theme.fish + PatrickF1/fzf.fish → functions/_fzf_*.fish + conf.d/fzf.fish + franciscolourenco/done → conf.d/done.fish + jorgebucaran/autopair.fish → functions/_autopair_*.fish + conf.d/autopair.fish + nickeb96/puffer-fish → functions/_puffer_fish_*.fish + conf.d/puffer.fish + +Do not run `fisher install` for these — it will overwrite the customized +versions. To update their behavior, edit the relevant bundled files directly. + +## fish_plugins Manifest + +The `fish_plugins` file at the config root contains only the Fisher manager +entry itself: jorgebucaran/fisher Plugin manager itself - catppuccin/fish Catppuccin Mocha color theme - PatrickF1/fzf.fish fzf integration for Fish - franciscolourenco/done Desktop notification when long commands finish - jorgebucaran/autopair.fish Auto-pair brackets and quotes - meaningful-ooo/sponge Remove failed commands from history - nickeb96/puffer-fish !! / !$ / ./ expansion -Run `fisher update` to update all plugins, or `fish-deps update` which -calls fisher update as its first step. +To update Fisher, run `fisher update` or `fish-deps update` which calls it +as its first step. --- @@ -1439,8 +1454,9 @@ This configuration is managed as a git repository. To deploy on a new machine: mv ~/.config/fish ~/.config/fish.bak # back up any existing config git clone https://git.rootiest.dev/rootiest/fish-config.git ~/.config/fish -Then open a new Fish shell. Fisher and all plugins install automatically on -first launch and the Catppuccin Mocha theme is applied. +Then open a new Fish shell. Fisher installs automatically on first launch +and the Catppuccin Mocha theme is applied. All other plugin functionality is +bundled directly with this config and requires no additional installation. ## Return Sentinel diff --git a/requirements.md b/requirements.md index 0462373..c787555 100644 --- a/requirements.md +++ b/requirements.md @@ -116,5 +116,9 @@ Non-standard applications required by this fish shell configuration. | Tool | Description | Notes | |------|-------------|-------| -| `fisher` | Fish plugin manager | Manages plugins via `fish_plugins` | -| `catppuccin/fish` | Catppuccin theme for Fish | Listed in `fish_plugins` | +| `fisher` | Fish plugin manager | Bootstrapped automatically on first run; only entry in `fish_plugins` | + +Plugin functionality (Catppuccin theme, FZF bindings, done notifications, autopair, puffer-fish +expansions) is bundled directly with this config in `functions/`, `conf.d/`, and `themes/`. These are +customized versions — do not install them via `fisher install`, as doing so would overwrite the +bundled modifications. -- 2.52.0 From 43123ff3904a5ed6e792e9515bd618beca6c80f2 Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:01:30 -0400 Subject: [PATCH 2/7] feat: add sponge via fisher; gitignore fisher-managed files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meaningful-ooo/sponge is now the one true Fisher-managed plugin, giving the bootstrap a concrete purpose. Its files (and functions/fisher.fish, completions/fisher.fish) are gitignored so Fisher owns them exclusively — no committed/managed conflict possible. Updates docs, index, requirements, and README to distinguish Fisher-managed plugins from bundled customized ones. --- .gitignore | 9 +++++++++ README.md | 2 +- docs/fish-config.index | 2 ++ docs/fish-config.md | 25 +++++++++++++++++-------- fish_plugins | 1 + requirements.md | 10 +++++----- 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 9f71d48..87cc2b5 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,15 @@ opinionated_catalog.md docs/superpowers +# ──────────────── Fisher-Managed Plugin Files ─────────────── +# Fisher writes these into the repo directory on install/update. +# They are owned by Fisher — do not commit them. +functions/fisher.fish +completions/fisher.fish +conf.d/sponge.fish +functions/_sponge*.fish +functions/sponge_*.fish + # ─────────────────── AI Sessions and Rules ────────────────── # Matches CLAUDE.md, .claud*, etc. [Cc][Ll][Aa][Uu][Dd][Ee].[Mm][Dd] diff --git a/README.md b/README.md index ccb7662..4a27f46 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This config layers on top of the CachyOS base Fish configuration and adds: - **Catppuccin Mocha** theming throughout (prompt, FZF, syntax highlighting) - **Starship** prompt with VI key bindings -- **Fisher** plugin manager bootstrapped automatically; plugin functionality (FZF bindings, Catppuccin theme, done notifications, autopair, puffer-fish) bundled directly with the config +- **Fisher** plugin manager bootstrapped automatically; manages `sponge` (failed-command history filter); FZF bindings, Catppuccin theme, done, autopair, and puffer-fish are bundled directly with the config as customized versions - **Smart CLI wrappers** that prefer modern tools (`eza`, `bat`, `btop`, `dust`, `prettyping`) with graceful fallbacks - **Auto Python venv** activation on directory change (direnv-aware) - **Kitty terminal** deep integration for splits, tabs, and SSH diff --git a/docs/fish-config.index b/docs/fish-config.index index 273c354..531404f 100644 --- a/docs/fish-config.index +++ b/docs/fish-config.index @@ -166,6 +166,8 @@ universal=## Fish Universal Variables # ── Section 8: Fisher Plugins ───────────────────────────────── plugins=# 8. FISHER PLUGINS fisher=# 8. FISHER PLUGINS +fisher-managed=## Fisher-Managed Plugins +sponge=## Fisher-Managed Plugins bundled-plugins=## Bundled Plugin Functionality fish-plugins-manifest=## fish_plugins Manifest diff --git a/docs/fish-config.md b/docs/fish-config.md index 57c6dcc..cf34cb0 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1417,12 +1417,21 @@ testing), run: Then open a new shell. +## Fisher-Managed Plugins + +The following plugins are fully managed by Fisher. Their files are installed +into the repo directory by Fisher and are listed in `.gitignore` — do not +commit them. Fisher installs and updates them automatically. + + jorgebucaran/fisher Plugin manager itself + meaningful-ooo/sponge Remove failed commands from history + ## Bundled Plugin Functionality -Plugin functionality is bundled directly with this config rather than managed -through Fisher. The bundled versions include customizations for Fish 4.x -compatibility and improved behavior that differ from their upstream releases. -Installing them through Fisher would overwrite these customizations. +The remaining plugin functionality is bundled directly with this config rather +than managed through Fisher. The bundled versions include customizations for +Fish 4.x compatibility and improved behavior that differ from their upstream +releases. Installing them through Fisher would overwrite these customizations. Bundled components and their upstream origins: @@ -1437,13 +1446,13 @@ versions. To update their behavior, edit the relevant bundled files directly. ## fish_plugins Manifest -The `fish_plugins` file at the config root contains only the Fisher manager -entry itself: +The `fish_plugins` file at the config root: jorgebucaran/fisher Plugin manager itself + meaningful-ooo/sponge Remove failed commands from history -To update Fisher, run `fisher update` or `fish-deps update` which calls it -as its first step. +To update all Fisher-managed plugins, run `fisher update` or `fish-deps +update` which calls it as its first step. --- diff --git a/fish_plugins b/fish_plugins index 594dfc0..5dd7d8e 100644 --- a/fish_plugins +++ b/fish_plugins @@ -1 +1,2 @@ jorgebucaran/fisher +meaningful-ooo/sponge diff --git a/requirements.md b/requirements.md index c787555..48ed99b 100644 --- a/requirements.md +++ b/requirements.md @@ -116,9 +116,9 @@ Non-standard applications required by this fish shell configuration. | Tool | Description | Notes | |------|-------------|-------| -| `fisher` | Fish plugin manager | Bootstrapped automatically on first run; only entry in `fish_plugins` | +| `fisher` | Fish plugin manager | Bootstrapped automatically on first run; manages `sponge` via `fish_plugins` | +| `meaningful-ooo/sponge` | Remove failed commands from history | Fisher-managed; files are gitignored | -Plugin functionality (Catppuccin theme, FZF bindings, done notifications, autopair, puffer-fish -expansions) is bundled directly with this config in `functions/`, `conf.d/`, and `themes/`. These are -customized versions — do not install them via `fisher install`, as doing so would overwrite the -bundled modifications. +Other plugin functionality (Catppuccin theme, FZF bindings, done notifications, autopair, puffer-fish +expansions) is bundled directly with this config in `functions/`, `conf.d/`, and `themes/` as +customized versions — do not install them via `fisher install`. -- 2.52.0 From d21aa4ed4be579d15dd7cb17c8b81ad0ec536b48 Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:03:24 -0400 Subject: [PATCH 3/7] =?UTF-8?q?chore:=20remove=20requirements.md=20(supers?= =?UTF-8?q?eded=20by=20=C2=A76=20dependency=20catalog=20and=20fish-deps)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.md | 124 ------------------------------------------------ 1 file changed, 124 deletions(-) delete mode 100644 requirements.md diff --git a/requirements.md b/requirements.md deleted file mode 100644 index 48ed99b..0000000 --- a/requirements.md +++ /dev/null @@ -1,124 +0,0 @@ -# Fish Config Requirements - -Non-standard applications required by this fish shell configuration. - -## Terminal Emulators - -| Tool | Description | Notes | -|------|-------------|-------| -| `kitty` | GPU-accelerated terminal emulator | Primary terminal; used heavily for tab/window management via `kitty @` | -| `wezterm` | Cross-platform GPU-accelerated terminal | Referenced as alternative | -| `konsole` | KDE terminal emulator | Used in `tab.fish` as fallback | - -## File & Directory Tools - -| Tool | Description | Notes | -|------|-------------|-------| -| `lsd` | `ls` replacement with icons and colors | Wraps `ls`, `l`, `ll`, `lt`, `lstree`, `lS`, `ltr` | -| `bat` | `cat` replacement with syntax highlighting | Wraps `cat` | -| `dust` | Disk usage analyzer (Rust) | Used in `du.fish` | -| `duf` | Disk usage/free utility | Used in `du.fish` | -| `dua` | Fast disk space analyzer | Used in `du.fish` | -| `trash` / `trash-cli` | Safe file deletion to trash | Wraps `rm` | - -## Editors - -| Tool | Description | Notes | -|------|-------------|-------| -| `nvim` | Neovim text editor | Primary editor; used in `edit`, `view`, `nlazyup`, `nvimup` | -| `kate` | KDE text editor | Abbreviation target | - -## Git & Version Control - -| Tool | Description | Notes | -|------|-------------|-------| -| `lazygit` | Terminal Git UI | Abbreviation `lg` | -| `gitui` | Fast terminal Git UI | `gitui.fish` wrapper | -| `clone-in-kitty` | Clone repos into a Kitty window | Used in `clone.fish`, `clonet.fish` | - -## System & Process Monitoring - -| Tool | Description | Notes | -|------|-------------|-------| -| `btop` | Modern resource monitor | Wraps `top` | -| `fastfetch` | System info display | `ffetch.fish`, `cffetch.fish` | -| `neofetch` | System info display (fallback) | Fallback in `ffetch.fish` | - -## Package Management - -| Tool | Description | Notes | -|------|-------------|-------| -| `paru` | AUR helper (Arch Linux) | Used in `install`, `search`, `upgrade`, `parur` | - -## Navigation & Search - -| Tool | Description | Notes | -|------|-------------|-------| -| `zoxide` | Smart `cd` with frecency | Initialized in `zoxide.fish` | -| `fzf` | Fuzzy finder | Used in `parur.fish` and elsewhere | -| `ripgrep` / `rg` | Fast line-oriented search | Wrapped in `rg.fish` | - -## Shell Prompt & Theme - -| Tool | Description | Notes | -|------|-------------|-------| -| `starship` | Cross-shell prompt | Initialized in `config.fish` | - -## Terminal Multiplexers - -| Tool | Description | Notes | -|------|-------------|-------| -| `zellij` | Terminal workspace/multiplexer | `zellij.fish` wrapper | - -## Network & Remote - -| Tool | Description | Notes | -|------|-------------|-------| -| `tailscale` | VPN client | `tailscale.fish`, `tailart.fish` | -| `curl` | HTTP/data transfer | Used in `bd-pull.fish` | - -## Developer & Productivity Tools - -| Tool | Description | Notes | -|------|-------------|-------| -| `chezmoi` | Dotfile manager | Numerous abbreviations | -| `llm` | CLI for LLM interaction | `llm.fish` wrapper | -| `cheat` | Cheatsheet viewer | `cheat.fish`, `cheat.conf.d` | -| `wakatime` | Developer time tracking | Initialized in `wakatime.fish` | -| `bd` / `beads` | Lightweight issue tracker | `bd-pull.fish`, abbreviations | -| `lazybeads` | Terminal UI for beads | Abbreviation target | -| `agy` | antigravity-cli AI assistant | `antigravity.fish` wrapper, abbreviations | -| `antigravity-ide` | antigravity-ide editor | `antigravity-ide.fish` wrapper, abbreviations | - -## Container Tools - -| Tool | Description | Notes | -|------|-------------|-------| -| `docker` | Container platform | `dops.fish`, abbreviations | -| `lazydocker` | Terminal Docker UI | `ld` function in `config.fish` | - -## Command Replacements / Enhancements - -| Tool | Description | Notes | -|------|-------------|-------| -| `ov` | Modern pager | Sets `$PAGER`; wraps `less` with hierarchy fallback | -| `prettyping` | Pretty `ping` wrapper | Wraps `ping` | -| `sudo-rs` | Rust rewrite of sudo | Wraps `sudo` | - -## Data & System Utilities - -| Tool | Description | Notes | -|------|-------------|-------| -| `jq` | JSON processor | Used in `bd-pull.fish` | -| `wl-colorpicker-plasma` | Wayland color picker (KDE) | `get-color.fish` | - -## Fish Shell Framework & Plugins - -| Tool | Description | Notes | -|------|-------------|-------| -| `fisher` | Fish plugin manager | Bootstrapped automatically on first run; manages `sponge` via `fish_plugins` | -| `meaningful-ooo/sponge` | Remove failed commands from history | Fisher-managed; files are gitignored | - -Other plugin functionality (Catppuccin theme, FZF bindings, done notifications, autopair, puffer-fish -expansions) is bundled directly with this config in `functions/`, `conf.d/`, and `themes/` as -customized versions — do not install them via `fisher install`. -- 2.52.0 From bceff8f4ecca31c3b90099c153c9c907aa7e2bf6 Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:23:29 -0400 Subject: [PATCH 4/7] feat(sponge): add three-layer privacy filtering for shell history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit conf.d/sponge_privacy.fish registers patterns and filters that prevent credentials from reaching fish history: Layer 1 (static regex, universal): auth flags, env assignments, credential-bearing URLs, Authorization headers, sshpass, docker login, openssl -passin/-passout Layer 2 (dynamic values, session globals): on the first prompt, after secrets.fish has loaded, reads the literal values of all exported credential-named vars (TOKEN, PASSWORD, SECRET, etc.), escapes them for regex, and merges them with the static patterns as a session global — auto-refreshes on login so rotated tokens are never stale Layer 3 (per-command filter, sponge_filter_secrets): catches credentials in variables exported mid-session (e.g. project .env) Also exempts functions/sponge_filter_secrets.fish from the sponge_* gitignore glob so our custom filter is committed alongside the config. --- .gitignore | 1 + conf.d/sponge_privacy.fish | 113 +++++++++++++++++++++++++++ docs/fish-config.index | 3 + docs/fish-config.md | 32 ++++++++ functions/sponge_filter_secrets.fish | 51 ++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 conf.d/sponge_privacy.fish create mode 100644 functions/sponge_filter_secrets.fish diff --git a/.gitignore b/.gitignore index 87cc2b5..44f8e03 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ completions/fisher.fish conf.d/sponge.fish functions/_sponge*.fish functions/sponge_*.fish +!functions/sponge_filter_secrets.fish # ─────────────────── AI Sessions and Rules ────────────────── # Matches CLAUDE.md, .claud*, etc. diff --git a/conf.d/sponge_privacy.fish b/conf.d/sponge_privacy.fish new file mode 100644 index 0000000..675fd5b --- /dev/null +++ b/conf.d/sponge_privacy.fish @@ -0,0 +1,113 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# ╭──────────────────────────────────────────────────────────╮ +# │ Sponge Privacy Pattern Registration │ +# ╰──────────────────────────────────────────────────────────╯ +# +# Two-layer approach to keeping credentials out of shell history: +# +# Layer 1 — Static patterns (registered as universal, persistent): +# Covers structural signatures: auth flags, env var assignments, +# credential-bearing URLs, Authorization headers, sshpass, etc. +# Patterns are added idempotently; user additions are preserved. +# +# Layer 2 — Dynamic secret values (registered as session globals): +# On the first prompt (after secrets.fish has loaded), reads the +# literal values of all exported variables whose names indicate +# credentials (TOKEN, PASSWORD, SECRET, KEY, etc.) and adds them +# as a session-scoped pattern overlay. Refreshes automatically +# each login, so rotated tokens never leave stale patterns behind. +# +# Layer 3 — Per-command filter (sponge_filter_secrets): +# Catches mid-session variables set after login — e.g. a token +# exported interactively or sourced from a project .env file. +# +# To add your own persistent patterns: +# set -U -a sponge_regex_patterns 'your-regex-here' + +if not status is-interactive + return +end + +# Only register if sponge is loaded +if not set -q sponge_version + return +end + +# ──────────────────── Layer 1: Static patterns ──────────────────── + +set -l _privacy_patterns + +# Common auth flags with values: --password x, --token x, --passphrase x, --api-key x +set -a _privacy_patterns '--(?:password|passwd|passphrase|token|secret|api[-_]key)(?:\s+|=)\S+' + +# Inline env var assignments with sensitive names: GITHUB_TOKEN=xxx, MY_API_KEY=abc +set -a _privacy_patterns '(?i)\b[A-Z][A-Z0-9_]*(?:PASSWORD|PASSWD|SECRET|TOKEN|API_KEY|PRIVATE_KEY|ACCESS_KEY|AUTH_KEY|CREDENTIAL)[A-Z0-9_]*=\S+' + +# Fish set with sensitive variable names: set -gx GITHUB_TOKEN xxx, set -U MY_SECRET yyy +set -a _privacy_patterns '(?i)set\s+-\S+\s+\S*(?:password|passwd|token|secret|api.?key|private.?key|access.?key|credential)\S*\s+\S+' + +# URLs with embedded credentials: https://user:password@host +set -a _privacy_patterns 'https?://[^:@\s]+:[^@\s]+@' + +# HTTP Authorization headers: curl -H "Authorization: Bearer xxx" +set -a _privacy_patterns 'curl\s.*[Aa]uthorization:' + +# Basic auth flags: curl -u user:pass, wget --user user --password pass +set -a _privacy_patterns '(?:curl|wget)\s.*(?:-u|--user)\s+\S+:\S+' + +# sshpass — exposes credentials as a CLI argument by design +set -a _privacy_patterns '\bsshpass\b' + +# Docker login with inline password +set -a _privacy_patterns 'docker\s+login\s.*(?:-p|--password)\s+\S+' + +# openssl passphrase arguments: -passin pass:xxx, -passout env:VAR +set -a _privacy_patterns 'openssl\s.*-pass(?:in|out)\s+\S+' + +# Idempotent registration into universal sponge_regex_patterns +for _pattern in $_privacy_patterns + if not contains -- $_pattern $sponge_regex_patterns + set -U -a sponge_regex_patterns $_pattern + end +end + +# ──────────── Layer 2: Dynamic secret values (session globals) ──────────── + +# Runs once on the first prompt — by which point config.fish and secrets.fish +# have fully loaded, so all secret env vars are in scope. +# Builds a session-scoped global that combines the universal static patterns +# with the literal values of any credential-holding env vars. Globals shadow +# universals in Fish, so the combined list is what sponge sees for this session. +function __sponge_register_secret_values --on-event fish_prompt + functions --erase __sponge_register_secret_values # run exactly once + + set -l secret_values + + set -l sensitive_vars (set --names --export | string match --regex -- \ + '(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)') + + for var in $sensitive_vars + set -l value $$var + # Skip empty, short, or path-like values + test (string length -- $value) -gt 8; or continue + string match --quiet --regex '^[/~]' -- $value; and continue + set -a secret_values (string escape --style=regex -- $value) + end + + if test (count $secret_values) -gt 0 + # Merge static universals + dynamic values into a session global + set -g sponge_regex_patterns $sponge_regex_patterns $secret_values + end +end + +# ──────────── Layer 3: Mid-session filter (sponge_filter_secrets) ───────── + +# Catches credentials in variables exported after login (e.g. project .env files). +# Only register if not already in sponge_filters. +if functions --query sponge_filter_secrets + if not contains -- sponge_filter_secrets $sponge_filters + set -U -a sponge_filters sponge_filter_secrets + end +end diff --git a/docs/fish-config.index b/docs/fish-config.index index 531404f..06dacd4 100644 --- a/docs/fish-config.index +++ b/docs/fish-config.index @@ -168,6 +168,9 @@ plugins=# 8. FISHER PLUGINS fisher=# 8. FISHER PLUGINS fisher-managed=## Fisher-Managed Plugins sponge=## Fisher-Managed Plugins +sponge-filtering=## Sponge History Filtering +history-filtering=## Sponge History Filtering +privacy=## Sponge History Filtering bundled-plugins=## Bundled Plugin Functionality fish-plugins-manifest=## fish_plugins Manifest diff --git a/docs/fish-config.md b/docs/fish-config.md index cf34cb0..8f4fa0f 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1426,6 +1426,38 @@ commit them. Fisher installs and updates them automatically. jorgebucaran/fisher Plugin manager itself meaningful-ooo/sponge Remove failed commands from history +## Sponge History Filtering + +Sponge removes failed commands from history and, via conf.d/sponge_privacy.fish, +also filters privacy-sensitive commands through three layers: + +Layer 1 — Static patterns (universal, persistent across sessions): +Commands matching any of these structural signatures are never recorded: + + --password / --token / --passphrase / --api-key flags with values + Inline env assignments: GITHUB_TOKEN=xxx, MY_API_KEY=abc + Fish set with sensitive names: set -gx GITHUB_TOKEN xxx + URLs with embedded credentials: https://user:pass@host + HTTP Authorization headers: curl -H "Authorization: ..." + Basic auth flags: curl -u user:pass + sshpass, docker login -p, openssl -passin/-passout + +Layer 2 — Dynamic secret values (session globals, refreshed each login): +On the first prompt, after secrets.fish has loaded, the literal values of +all exported variables whose names suggest credentials (TOKEN, PASSWORD, +SECRET, API_KEY, etc.) are collected, regex-escaped, and added as a +session-scoped overlay. Because globals shadow universals in Fish, the +combined list is what sponge sees. Rotating a token takes effect on the +next login automatically. + +Layer 3 — Per-command filter (sponge_filter_secrets): +Catches credentials in variables exported after login, such as tokens +sourced from a project .env file mid-session. + +To add your own persistent patterns: + + set -U -a sponge_regex_patterns 'your-regex-here' + ## Bundled Plugin Functionality The remaining plugin functionality is bundled directly with this config rather diff --git a/functions/sponge_filter_secrets.fish b/functions/sponge_filter_secrets.fish new file mode 100644 index 0000000..b6e7f36 --- /dev/null +++ b/functions/sponge_filter_secrets.fish @@ -0,0 +1,51 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# sponge_filter_secrets +# +# DESCRIPTION +# Custom sponge filter that prevents commands from being stored in history +# when they contain the literal value of any exported environment variable +# whose name indicates it holds a credential (TOKEN, PASSWORD, SECRET, +# API_KEY, etc.). This catches shell-expansion leakage where a variable +# value is embedded directly in the command string at execution time — a +# case that static regex patterns cannot cover. +# +# Any variable whose name matches the sensitive-name heuristic and whose +# value is longer than 8 characters (excluding bare paths) is checked. +# The value is escaped for literal regex matching before comparison. +# +# ARGUMENTS +# command The exact command that was entered +# exit_code Exit code of the command (unused) +# previously_in_history "true"/"false" flag (unused) +# +# RETURNS +# 0 Command contains a secret value — filter out of history +# 1 No secret value found — keep in history +# +# EXAMPLE +# # Register with sponge (done automatically by conf.d/sponge_privacy.fish): +# set -U -a sponge_filters sponge_filter_secrets + +function sponge_filter_secrets --argument-names command + # Find all exported variables with security-sensitive names + set -l sensitive_vars (set --names --export | string match --regex -- \ + '(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)') + + for var in $sensitive_vars + set -l value $$var + + # Skip empty, short, or path-like values — not real credentials + test (string length -- $value) -gt 8; or continue + string match --quiet --regex '^[/~]' -- $value; and continue + + # Filter if the literal value appears anywhere in the command + if string match --quiet --regex -- (string escape --style=regex -- $value) $command + return 0 + end + end + + return 1 +end -- 2.52.0 From 1637dc31226679c76a96af5e2e1d4d6e9fda42e2 Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:24:31 -0400 Subject: [PATCH 5/7] fix(sponge): use $$var[1] to avoid test arg-count error on array vars --- conf.d/sponge_privacy.fish | 4 +++- functions/sponge_filter_secrets.fish | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/conf.d/sponge_privacy.fish b/conf.d/sponge_privacy.fish index 675fd5b..f57da80 100644 --- a/conf.d/sponge_privacy.fish +++ b/conf.d/sponge_privacy.fish @@ -89,7 +89,9 @@ function __sponge_register_secret_values --on-event fish_prompt '(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)') for var in $sensitive_vars - set -l value $$var + # Take only the first element — array vars yield multiple values which + # would make `test (string length ...)` receive too many arguments. + set -l value $$var[1] # Skip empty, short, or path-like values test (string length -- $value) -gt 8; or continue string match --quiet --regex '^[/~]' -- $value; and continue diff --git a/functions/sponge_filter_secrets.fish b/functions/sponge_filter_secrets.fish index b6e7f36..fd4d171 100644 --- a/functions/sponge_filter_secrets.fish +++ b/functions/sponge_filter_secrets.fish @@ -35,7 +35,8 @@ function sponge_filter_secrets --argument-names command '(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)') for var in $sensitive_vars - set -l value $$var + # Take only the first element — array vars yield multiple values + set -l value $$var[1] # Skip empty, short, or path-like values — not real credentials test (string length -- $value) -gt 8; or continue -- 2.52.0 From 8911146d3f0577bedc36111fa4ebf4c049be281f Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:26:00 -0400 Subject: [PATCH 6/7] fix(sponge): guard set -q value[1] before string length to handle unset vars --- conf.d/sponge_privacy.fish | 7 ++++--- functions/sponge_filter_secrets.fish | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/conf.d/sponge_privacy.fish b/conf.d/sponge_privacy.fish index f57da80..e3929f4 100644 --- a/conf.d/sponge_privacy.fish +++ b/conf.d/sponge_privacy.fish @@ -89,10 +89,11 @@ function __sponge_register_secret_values --on-event fish_prompt '(?i)(?:TOKEN|PASSWORD|PASSWD|SECRET|API[_-]KEY|PRIVATE[_-]KEY|ACCESS[_-]KEY|AUTH[_-]KEY|CREDENTIAL|KOPIA_PASSWORD)') for var in $sensitive_vars - # Take only the first element — array vars yield multiple values which - # would make `test (string length ...)` receive too many arguments. + # Take only the first element — array vars yield multiple values. set -l value $$var[1] - # Skip empty, short, or path-like values + # If the var is unset or holds an empty list, value has no elements; + # set -q catches that before string length receives zero arguments. + set -q value[1]; or continue test (string length -- $value) -gt 8; or continue string match --quiet --regex '^[/~]' -- $value; and continue set -a secret_values (string escape --style=regex -- $value) diff --git a/functions/sponge_filter_secrets.fish b/functions/sponge_filter_secrets.fish index fd4d171..66ffa76 100644 --- a/functions/sponge_filter_secrets.fish +++ b/functions/sponge_filter_secrets.fish @@ -37,8 +37,11 @@ function sponge_filter_secrets --argument-names command for var in $sensitive_vars # Take only the first element — array vars yield multiple values set -l value $$var[1] + # Guard before string length: if var is unset, value is an empty list + # and string length receives zero arguments, breaking test. + set -q value[1]; or continue - # Skip empty, short, or path-like values — not real credentials + # Skip short or path-like values — not real credentials test (string length -- $value) -gt 8; or continue string match --quiet --regex '^[/~]' -- $value; and continue -- 2.52.0 From 13eb93ce12fce746faf53790212d011caf9b8ff5 Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 8 Jun 2026 22:29:20 -0400 Subject: [PATCH 7/7] fix(sponge): escape leading -- in auth-flag pattern; purge stale -- patterns on load --- conf.d/sponge_privacy.fish | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/conf.d/sponge_privacy.fish b/conf.d/sponge_privacy.fish index e3929f4..49a1d17 100644 --- a/conf.d/sponge_privacy.fish +++ b/conf.d/sponge_privacy.fish @@ -40,7 +40,9 @@ end set -l _privacy_patterns # Common auth flags with values: --password x, --token x, --passphrase x, --api-key x -set -a _privacy_patterns '--(?:password|passwd|passphrase|token|secret|api[-_]key)(?:\s+|=)\S+' +# Use \- to avoid the pattern string starting with --, which string match +# would misinterpret as an unknown option flag. +set -a _privacy_patterns '\-\-(?:password|passwd|passphrase|token|secret|api[-_]key)(?:\s+|=)\S+' # Inline env var assignments with sensitive names: GITHUB_TOKEN=xxx, MY_API_KEY=abc set -a _privacy_patterns '(?i)\b[A-Z][A-Z0-9_]*(?:PASSWORD|PASSWD|SECRET|TOKEN|API_KEY|PRIVATE_KEY|ACCESS_KEY|AUTH_KEY|CREDENTIAL)[A-Z0-9_]*=\S+' @@ -66,6 +68,14 @@ set -a _privacy_patterns 'docker\s+login\s.*(?:-p|--password)\s+\S+' # openssl passphrase arguments: -passin pass:xxx, -passout env:VAR set -a _privacy_patterns 'openssl\s.*-pass(?:in|out)\s+\S+' +# Remove any previously stored patterns that begin with -- ; string match +# passes patterns before its own -- sentinel and would treat them as flags. +for _i in (seq (count $sponge_regex_patterns) -1 1) + if string match --quiet -- '--*' $sponge_regex_patterns[$_i] + set -Ue sponge_regex_patterns[$_i] + end +end + # Idempotent registration into universal sponge_regex_patterns for _pattern in $_privacy_patterns if not contains -- $_pattern $sponge_regex_patterns -- 2.52.0