{pkgs, ...}: {
  users.users.someone.packages = with pkgs; [
    fish
    grc
    (writeShellScriptBin "hx" ''
      ${pkgs.helix}/bin/hx "$@"
    '')
  ];
  xdg.configFile = {
    # ── Modular fish conf.d ──────────────────────────────────────────────────
    # Each file under fish/conf.d/ is sourced automatically by fish at startup,
    # sorted lexicographically. We use numeric prefixes for ordering.

    # 00: Environment variables
    "fish/conf.d/00-env.fish".text = ''
      set -gx GNUPGHOME $HOME/.config/gnupg
      set -gx NIXPKGS_ALLOW_UNFREE 1
      set -gx NIXPKGS_ALLOW_INSECURE 1
      set -gx EDITOR nvim
      set -gx VISUAL nvim
      set -g fish_greeting
    '';

    # 01: SOPS secret loader
    "fish/conf.d/01-secrets.fish".text = ''
      for secret in discordo openrouter github twt gemini context7 exa
        if test -f /run/secrets/$secret
          set -l val (cat /run/secrets/$secret)
          set -l up (string upper $secret)
          switch $secret
            case discordo
              set -gx DISCORDO_TOKEN $val
              set -gx OXICORD_TOKEN $val
            case openrouter gemini context7 exa
              set -gx "$up"_API_KEY $val
            case '*'
              set -gx "$up"_TOKEN $val
          end
        end
      end
    '';

    # 02: Vi key bindings and cursor shapes
    "fish/conf.d/02-key-bindings.fish".text = ''
      fish_vi_key_bindings

      function fish_user_key_bindings
        for mode in insert default
          bind -M $mode ctrl-backspace backward-kill-word
          bind -M $mode ctrl-delete kill-word
          bind -M $mode alt-backspace backward-kill-token
          bind -M $mode alt-delete kill-token
          bind -M $mode ctrl-z undo
        end
        bind -M insert \cx\ce edit_command_buffer
        bind -M default \cx\ce edit_command_buffer
        bind -M default up history-prefix-search-backward
        bind -M default down history-prefix-search-forward
      end

      set fish_cursor_default block
      set fish_cursor_insert line
      set fish_cursor_replace_one underscore
      set fish_cursor_visual block
    '';

    # 03: Syntax highlighting colors
    "fish/conf.d/03-syntax.fish".text = ''
      set -g fish_color_autosuggestion brblack
      set -g fish_color_command blue
      set -g fish_color_error red
      set -g fish_color_param normal
      set -g fish_color_search_match --background=normal
    '';

    # 04: Plugin settings
    "fish/conf.d/04-plugins.fish".text = ''
      set -Ux fifc_editor nvim
      set -U fifc_keybinding \cv
      set -g __done_min_cmd_duration 10000
    '';
    "fish/functions/extract.fish" = {
      text = ''
                function extract
                  if test -f "$argv[1]"
                    set file "$argv[1]"
                    set filename (basename "$file")
                    set dirname (string replace -r '\.tar\.bz2$|\.tar\.gz$|\.tbz2$|\.tgz$|\.tar$|\.bz2$|\.gz$|\.zip$|\.Z$|\.7z$|\.xz$|\.rar$' "" "$filename")
                mkdir -p "$dirname"
                switch $file
                    case "*.tar.bz2"
                        tar xjf "$file" -C "$dirname"
                    case "*.tar.gz"
                        tar xzf "$file" -C "$dirname"
                    case "*.tbz2"
                        tar xjf "$file" -C "$dirname"
                    case "*.tgz"
                        tar xzf "$file" -C "$dirname"
                    case "*.tar"
                        tar xf "$file" -C "$dirname"
                    case "*.bz2"
                        bunzip2 -c "$file" >"$dirname/$dirname"
                    case "*.gz"
                        gunzip -c "$file" >"$dirname/$dirname"
                    case "*.zip"
                        unzip "$file" -d "$dirname"
                    case "*.Z"
                        uncompress -c "$file" >"$dirname/$dirname"
                    case "*.7z"
                        7z x "$file" -o"$dirname"
                    case "*.xz"
                        xz -dc "$file" >"$dirname/$dirname"
                    case "*.rar"
                        unrar x "$file" "$dirname"
                    case "*"
                        echo "'$file' cannot be extracted via extract()"
                end
           else
                echo "'$argv[1]' is not a valid file"
            end
        end
      '';
    };
    "fish/functions/weather.fish" = {
      text = ''
                function weather
            if test "$argv[1]" = week
                wthrr -f w $argv[2]
            else if test -z "$argv[1]"
                wthrr -f d
                wthrr -f t
            else
                wthrr -f d $argv[1]
                wthrr -f t $argv[1]
            end
        end
      '';
    };
    "fish/functions/fcd.fish" = {
      text = ''
        function fcd
          set -l dir (fd --type d | sk | string trim)
          if test -n "$dir"
            z $dir
          end
        end
      '';
    };
    "fish/functions/installed.fish" = {
      text = ''
        function installed
          nix-store --query --requisites /run/current-system/ | string replace -r '.*?-(.*)' '$1' | sort | uniq | sk
        end
      '';
    };
    "fish/functions/installedall.fish" = {
      text = ''
        function installedall
          nix-store --query --requisites /run/current-system/ | sk | wl-copy
        end
      '';
    };
    "fish/functions/fm.fish" = {
      text = ''
        function fm
          set -l tmp (mktemp -t "yazi-cwd.XXXXX")
          yazi $argv --cwd-file $tmp
          set -l cwd (cat $tmp)
          if test -n "$cwd" -a "$cwd" != "$PWD"
            cd $cwd
          end
          rm -f $tmp
        end
      '';
    };
    "fish/functions/gitgrep.fish" = {
      text = ''
        function gitgrep
          git ls-files | rg $argv
        end
      '';
    };
    # 05: Aliases
    "fish/conf.d/05-aliases.fish".text = ''
      alias cleanup="sudo nix-collect-garbage --delete-older-than 1d"
      alias listgen="sudo nix-env -p /nix/var/nix/profiles/system --list-generations"
      alias nixremove="nix-store --gc"
      alias bloat="nix path-info -Sh /run/current-system"
      alias cleanram="sudo sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches'"
      alias trimall="sudo fstrim -va"
      alias c="clear"
      alias add="git add ."
      alias commit="git commit"
      alias push="git push"
      alias pull="git pull"
      alias diff="git diff --staged"
      alias gcld="git clone --depth 1"
      alias koji="meteor"
      alias gitui="lazygit"
      alias ls="eza -lah --grid -s modified --smart-group --group-directories-first --icons"
      alias l="eza -ah --grid -s modified --smart-group --group-directories-first --icons"
      alias tree="eza --tree --icons --tree"
      alias cat="${pkgs.bat}/bin/bat --paging=never --theme gruvbox-dark"
      alias us="systemctl --user"
      alias rs="sudo systemctl"
      alias zed="zeditor"
      alias nl="nl -ba"
      alias cp="cp -riv"
      alias mv="mv -iv"
      alias mkdir="mkdir -pv"
      alias less="bat -p --theme gruvbox-dark"
      alias myip="curl ip.severijnse.eu"
      alias pscpu="ps -eo pid,ppid,cmd,%mem,%cpu,etime --sort=-%cpu | head -n 21"
      alias psmem="ps -eo pid,ppid,cmd,%mem,%cpu,etime --sort=-%mem | head -n 21"
      alias weer="weather"
      alias v="nvim"
      alias copy="wl-copy"
      alias send="croc"
      alias img="loupe"
      alias cat-md="glow"
      alias du="dust"
      alias df="duf"
    '';
  };
}
