3.7 KiB
fish docs index: initialization files (config.fish is always sourced)
Metadata
| Field | Value |
|---|---|
| Institute | Molodetz Institute — Research & Retrieval Division |
| Subject | shell-multiplexer-auto-attach |
| Document type | Source Dossier |
| Tier | 1 |
| Source title | fish documentation index (3.4) |
| Source URL | https://fishshell.com/docs/3.4/index.html |
| Source publisher / author | fish-shell project (fishshell.com) |
| Source type | vendor docs |
| Source date (published) | Unknown — not stated |
| Date researched | 2026-10-10 |
| Researched by | tai (agent), model: Claude Sonnet 5, on behalf of retoor |
| Research method | web search synthesis |
| Confidence | High |
| Status | Active |
Summary
fish's official docs state plainly that its startup files are read "on the startup of every shell, not just interactive ones" — unlike bash's .bashrc (interactive-only, non-login) or .profile/.bash_profile (login-only). ~/.config/fish/config.fish (respecting $XDG_CONFIG_HOME) is read on every fish invocation, interactive or not, login or not, and is explicitly the right place to put code that should run unconditionally — with status is-interactive/status is-login as the documented way to scope behavior within it. Snippets in ~/.config/fish/conf.d/ run before config.fish, which runs last so it can override them.
Key findings
- This is the single most consequential difference from bash/zsh for this project:
.bashrcand.zshrcare themselves gated to interactive shells by the shell's own startup logic (roughly), so the bash/zsh plexer block'scase "$-" in *i*)guard is a belt-and-suspenders check on top of a file that mostly only gets read in the right context anyway.config.fishhas no such built-in gating — it runs unconditionally, so thestatus is-interactiveguard insideFISH_PLEXER_BLOCKis doing all of the interactivity-scoping work, not just reinforcing it. - The fish FAQ (per the search tool's synthesis) explicitly calls out this exact contrast with
.bashrc/.profile, confirming it's a documented, intentional design difference in fish, not an edge case this research happened to stumble on. ~/.config/fish/config.fish's location follows$XDG_CONFIG_HOME— directly informingshell_rc_path()in tai.py, which checksos.environ.get("XDG_CONFIG_HOME")before falling back to~/.config.
Pros / strengths
- Official documentation stating the behavior plainly and explicitly contrasting it with bash/zsh conventions, removing any ambiguity about whether the interactivity guard is optional.
Cons / limitations / caveats
- A third-party pyenv wiki page (github-pyenv-wiki-unix-shell-initialization.md, also Tier 1) lists a fuller startup-file ordering (system/user
conf.d,vendor_conf.d,/etc/fish/config.fish, then~/.config/fish/config.fish) that this search could not independently confirm against the official docs; treated as secondary/unconfirmed per that dossier's own caveat, not relied on for anything load-bearing here.
Relevance to tai
This is the finding that made the status is-interactive guard non-optional in FISH_PLEXER_BLOCK: without it, installing the plexer hook into config.fish would attempt to exec tai.py --plexer-wrap on every fish invocation, including scripts, fish -c, and tooling that shells out to fish non-interactively — exactly the failure mode the user explicitly wanted to avoid. Confirmed end-to-end by the FishNonInteractiveSkipTest integration test in tests/bashrc.py, which runs a plain fish -c 'echo marker' (no explicit sourcing needed, since config.fish always runs) and asserts no plexer socket gets created.
Changelog
| Date | Change |
|---|---|
| 2026-10-10 | Initial dossier created. |