diff --git a/community/00_contributing/03_zsh_plugin_standard.mdx b/community/00_contributing/03_zsh_plugin_standard.mdx
index 1a148a3a..6f84aad3 100644
--- a/community/00_contributing/03_zsh_plugin_standard.mdx
+++ b/community/00_contributing/03_zsh_plugin_standard.mdx
@@ -517,7 +517,7 @@ Plugin managers may export the parameter `$ZPFX`, holding a path to a directory
dedicated to user-land software, giving `$ZPFX/bin`, `$ZPFX/lib`,
`$ZPFX/share`, and so on. The suggested directory name is `polaris`. Zi uses
that name and places the directory at
-`${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris` by default.
+`${ZI[HOME_DIR]}/polaris` by default.
Users can then configure hooks to invoke, for example,
`make PREFIX=$ZPFX install` on clone and update, to install software such as
@@ -529,7 +529,7 @@ across accounts and machines.
Facts related to `$ZPFX`:
1. `export ZPFX="$HOME/polaris"`, or
- `${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris`
+ `${ZI[HOME_DIR]}/polaris`
2. `make PREFIX=$ZPFX install`
diff --git a/docs/getting_started/01_installation.mdx b/docs/getting_started/01_installation.mdx
index bf4d5d5b..a9f450d8 100644
--- a/docs/getting_started/01_installation.mdx
+++ b/docs/getting_started/01_installation.mdx
@@ -46,9 +46,15 @@ sh -c "$(curl -fsSL get.zshell.dev)" -- -a loader
The installer will download the loader and add the snippet below to the .zshrc file.
```zsh showLineNumbers
-if [[ -r "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" ]]; then
- source "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" && zzinit
+if [[ -n ${XDG_CONFIG_HOME:-} && $XDG_CONFIG_HOME == /* ]]; then
+ ZI_LOADER_CONFIG_HOME="$XDG_CONFIG_HOME/zi"
+else
+ ZI_LOADER_CONFIG_HOME="$HOME/.config/zi"
fi
+if [[ -r "$ZI_LOADER_CONFIG_HOME/init.zsh" ]]; then
+ source "$ZI_LOADER_CONFIG_HOME/init.zsh" && zzinit
+fi
+unset ZI_LOADER_CONFIG_HOME
```
Then reload the shell with: `exec zsh`. All done!
@@ -95,11 +101,20 @@ sh -c "$(curl -fsSL get.zshell.dev)" -- -a zunit
Set up the install location and create a directory:
+The assignment below explicitly selects a fresh XDG-style home. It does not
+migrate an existing `$HOME/.zi` installation. For automatic legacy detection,
+use the loader above or source your existing Zi checkout without overriding
+`ZI[HOME_DIR]`.
+
```zsh showLineNumbers
typeset -Ag ZI
-typeset -gx ZI[HOME_DIR]="${XDG_DATA_HOME:-${HOME}/.local/share}/zi"
-typeset -gx ZI[BIN_DIR]="${ZI[HOME_DIR]}/bin"
-command mkdir -p "$ZI[BIN_DIR]"
+if [[ -n ${XDG_DATA_HOME:-} && $XDG_DATA_HOME == /* ]]; then
+ ZI[HOME_DIR]="$XDG_DATA_HOME/zi"
+else
+ ZI[HOME_DIR]="$HOME/.local/share/zi"
+fi
+ZI[BIN_DIR]="${ZI[HOME_DIR]}/bin"
+command mkdir -p -- "${ZI[BIN_DIR]}"
```
For security reasons run function compaudit to check if the [completion system][completion-system] would use files owned by root or by the current user, or files in directories that are world or group-writable.
@@ -107,9 +122,9 @@ For security reasons run function compaudit to check if the [completi
If failed, then set the current user as the owner of directories, then remove group/others write permissions, and clone the repository:
```zsh showLineNumbers
-compaudit | xargs chown -R "$(whoami)" "$ZI[HOME_DIR]"
-compaudit | xargs chmod -R go-w "$ZI[HOME_DIR]"
-command git clone https://github.com/z-shell/zi.git "$ZI[BIN_DIR]"
+compaudit | xargs chown -R "$(whoami)" "${ZI[HOME_DIR]}"
+compaudit | xargs chmod -R go-w "${ZI[HOME_DIR]}"
+command git clone https://github.com/z-shell/zi.git "${ZI[BIN_DIR]}"
```
### Enable {/* #i-classfas-fa-spinner-fa-spini-enable */}
diff --git a/docs/guides/02_customization.mdx b/docs/guides/02_customization.mdx
index be7f9379..19f006f6 100644
--- a/docs/guides/02_customization.mdx
+++ b/docs/guides/02_customization.mdx
@@ -38,11 +38,11 @@ source "${ZI[BIN_DIR]}/zi.zsh"
| Hash Field | Default | Description |
| ------------------------------------ | ----------------------------- | ---------------------------------------------- |
-| `ZI[HOME_DIR]` | `${XDG_DATA_HOME:-$HOME/.local/share}/zi` | Where Zi should create all working directories |
+| `ZI[HOME_DIR]` | Legacy `$HOME/.zi`, otherwise XDG data `zi` | Where Zi should create all working directories |
| `ZI[BIN_DIR]` | `$ZI[HOME_DIR]/bin` | Directory where Zi code resides |
| `ZI[COMPLETIONS_DIR]` | `$ZI[HOME_DIR]/completions` | Completion working directory |
-| `ZI[CACHE_DIR]` | `$HOME/.cache/zi` | Cache directory |
-| `ZI[CONFIG_DIR]` | `$HOME/.config/zi` | Directory for configuration files |
+| `ZI[CACHE_DIR]` | `${XDG_CACHE_HOME:-$HOME/.cache}/zi` | Cache directory |
+| `ZI[CONFIG_DIR]` | `${XDG_CONFIG_HOME:-$HOME/.config}/zi` | Directory for configuration files |
| `ZI[MAN_DIR]` | `$ZPFX/man` | Directory to store manpages |
| `ZI[LOG_DIR]` | `$ZI[CACHE_DIR]/log` | Directory to store log files |
| `ZI[PLUGINS_DIR]` | `$ZI[HOME_DIR]/plugins` | Plugins working directory |
@@ -55,6 +55,59 @@ source "${ZI[BIN_DIR]}/zi.zsh"
```
+:::info Loader defaults
+
+If you install with the [loader](/docs/getting_started/installation#loader),
+`init.zsh` sets `ZI[REPOSITORY]`, `ZI[STREAM]`, `ZI[HOME_DIR]`, and
+`ZI[BIN_DIR]` before Zi is cloned, because those four decide what to fetch and
+where. It mirrors the core home resolver. Every other field in this table,
+including cache and configuration, is owned by `zi.zsh`.
+
+To override any field, assign it in your `.zshrc` **before** sourcing the
+loader or `zi.zsh`.
+
+:::
+
+#### Path precedence and XDG validity
+
+Zi resolves paths in this order:
+
+1. A non-empty explicit `ZI[...]` path, or explicit `ZPFX`, wins.
+2. For `ZI[HOME_DIR]`, a recognized legacy `$HOME/.zi` installation remains
+ active so code, plugins, snippets, completions, and modules are not split.
+3. Otherwise Zi uses the matching XDG base when its value is an absolute path.
+4. An unset, empty, or relative XDG value uses the specification fallback:
+ `$HOME/.local/share`, `$HOME/.cache`, or `$HOME/.config`.
+
+Zi does not use `ZDOTDIR` for application data, cache, or configuration. It is
+the home for Zsh startup files, not a Zi storage base. XDG destinations do not
+need to exist in advance; Zi creates its new application roots privately and
+does not change permissions on a pre-existing base directory.
+
+`XDG_ZI_HOME`, `XDG_ZI_CACHE`, and `XDG_ZI_CONFIG` are exported compatibility
+outputs derived from the resolved `ZI[...]` values. They are not configuration
+inputs. Set `ZI[HOME_DIR]`, `ZI[CACHE_DIR]`, or `ZI[CONFIG_DIR]` instead.
+
+#### Legacy and both-present homes
+
+Zi never moves, merges, deletes, or overwrites an installation merely because
+an XDG location is available. If only a recognized `$HOME/.zi` installation
+exists, Zi keeps using it and `zi zstatus` displays a migration hint outside
+the shell startup path.
+
+If both legacy and XDG homes contain Zi data, Zi uses an explicit configuration
+or the sourced `ZI[BIN_DIR]` identity. An external checkout without either
+identity conservatively selects the legacy home. `zi zstatus` reports the
+ambiguity and selected root. To resolve it, set `ZI[HOME_DIR]` before sourcing
+the loader or `zi.zsh`; do not combine roots manually while shells are using
+them.
+
+An automatic migration command is not part of path resolution. Follow
+[z-shell/zi#429](https://github.com/z-shell/zi/issues/429) for the planned
+explicit, recoverable migration workflow. Until it is released, keep a backup
+and treat changing `ZI[HOME_DIR]` as selecting another root, not as moving the
+existing one.
+
### Modify settings {/* #modify-settings */}
```mdx-code-block
@@ -67,6 +120,7 @@ source "${ZI[BIN_DIR]}/zi.zsh"
| `ZI[COMPINIT_OPTS]` | `undefined` | Options for `compinit` call (e.g: done by `zicompinit`), commonly used with `-C` to speed up loading |
| `ZI[MUTE_WARNINGS]` | `undefined` | If set to `1`, mutes some warnings, specifically the `plugin already registered` warning |
| `ZI[PKG_OWNER]` | `z-shell` | Owner of the [packages][] (`zi pack …`) |
+| `ZI[LOADER_HISTORY]` | `1` | Loader only. Set to `0` before sourcing `init.zsh` to leave `HISTFILE`, `SAVEHIST`, and `HISTSIZE` entirely to your own configuration |
```mdx-code-block
@@ -103,7 +157,7 @@ zi as'null' wait'1' lucid for \
tj/git-extras
```
-The target directory for installed files is `$ZPFX` - `${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris` by default.
+The target directory for installed files is `$ZPFX` - `${ZI[HOME_DIR]}/polaris` by default.
With [meta-plugins][z-shell/z-a-meta-plugins] consisting of:
diff --git a/docs/guides/syntax/01_standard.mdx b/docs/guides/syntax/01_standard.mdx
index 465cac88..d7108d81 100644
--- a/docs/guides/syntax/01_standard.mdx
+++ b/docs/guides/syntax/01_standard.mdx
@@ -130,7 +130,7 @@ The `make'…'` ice could also be: `make"install PREFIX=$ZPFX"`, if "install" wo
:::info
-[$ZPFX][global-parameter-with-prefix] is provided by Zi, it is set to `${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris` by default. However, it can be changed by specifying the `$ZPFX=` target.
+[$ZPFX][global-parameter-with-prefix] is provided by Zi, it is set to `${ZI[HOME_DIR]}/polaris` by default. However, it can be changed by specifying the `$ZPFX=` target.
:::
diff --git a/ecosystem/annexes/1_bin_gem_node.mdx b/ecosystem/annexes/1_bin_gem_node.mdx
index e08c4df6..0d094635 100644
--- a/ecosystem/annexes/1_bin_gem_node.mdx
+++ b/ecosystem/annexes/1_bin_gem_node.mdx
@@ -52,7 +52,7 @@ zi ice as'program' from'gh-r' sbin'fzf'
zi load junegunn/fzf
```
-The `$PATH` will remain unchanged and a forwarder-script of `fzf` shim will be created in `$ZPFX/bin` (`${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris/bin` by default), which is being already added to the `$PATH` by Zi when it is being sourced:
+The `$PATH` will remain unchanged and a forwarder-script of `fzf` shim will be created in `$ZPFX/bin` (`${ZI[HOME_DIR]}/polaris/bin` by default), which is being already added to the `$PATH` by Zi when it is being sourced:
```zsh title="cat $ZPFX/bin/fzf" showLineNumbers
#!/usr/bin/env zsh
@@ -117,7 +117,7 @@ View all currently registered:
sbin'[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-script}]; …'
```
-Creates the so-called `shim` known from `rbenv` – a wrapper script that forwards the call to the actual binary. The script is created always under the same, standard, and single `$PATH` entry: `$ZPFX/bin` (which is `${XDG_DATA_HOME:-$HOME/.local/share}/zi/polaris/bin` by default). The flags have the same meaning as with `fbin'…'` ice.
+Creates the so-called `shim` known from `rbenv` – a wrapper script that forwards the call to the actual binary. The script is created always under the same, standard, and single `$PATH` entry: `$ZPFX/bin` (which is `${ZI[HOME_DIR]}/polaris/bin` by default). The flags have the same meaning as with `fbin'…'` ice.