Zsh session history — directory layout & migration spec

Move per-session HISTFILE blobs from $HOME root into ~/.zsh/history; align cleanup, docs, and tooling

Zsh session history — directory layout & migration spec

1. Problem

Note: fc -l / fc -ln and arrow-key widgets read zsh’s in-memory + current HISTFILE for that session, not “all session files merged.” Fragmentation across many tiny files is a separate concern from “where on disk they live,” but one canonical directory still improves hygiene and backup/ignore rules.

2. Is it safe to use a folder like ~/.zsh/?

Yes, provided you do it in this order:

  1. Define a single base directory (recommended below).
  2. Create the directory before any zsh sets HISTFILE (first line of history-related block in .zshrc, or mkdir -p guard).
  3. Point HISTFILE at the new path for new shells only after you are ready.
  4. Migrate existing files (optional but recommended) so old history is not orphaned.
  5. Update every glob/script that matched $HOME/.zsh_history_* to the new location.

Risks if you skip steps:

Risk Mitigation
New shells write to a new path while old files stay in ~ One-time mv + update cleanup to delete both patterns during a transition window, or migrate first then change HISTFILE.
Running shells still have old HISTFILE in memory Close terminals (or exec zsh) after changing .zshrc; avoid editing HISTFILE while sessions are writing.
Cleanup deletes wrong files Scope glob to $ZSH_HISTORY_DIR/.zsh_history_* only; never find $HOME.
Tools like sas / csh / si still grep $HOME Grep ~/.zshrc for zsh_history, HISTFILE, sas, csh and update to the shared variable.

Permissions: chmod 700 ~/.zsh is reasonable; history can contain secrets (tokens, paths).

3. Target layout (recommended)

Use a hidden config tree under home, not a visible ~/zsh folder (avoid clashing with a real project named zsh).

Path Purpose
$HOME/.zsh/ All machine-local zsh state (gitignored in dotfiles if you version only selective files).
$HOME/.zsh/history/ Per-session HISTFILE files only.
$HOME/.zsh/history/.zsh_history_<timestamp>_<pid> Same naming convention as today, only the directory changes.

Optional (XDG-minded): $HOME/.local/state/zsh/history/ — equally valid; pick one and document it. This spec standardizes on ~/.zsh/history/ for simplicity and discoverability next to future ~/.zsh/*.zsh includes.

Environment variables (contract)

Define once in ~/.zshrc (early):

# Zsh state root (machine-local)
export ZSH_STATE_DIR="${ZSH_STATE_DIR:-$HOME/.zsh}"
# Per-session history files
export ZSH_HISTORY_DIR="${ZSH_HISTORY_DIR:-$ZSH_STATE_DIR/history}"
mkdir -p "$ZSH_HISTORY_DIR"

Per-session files (timestamp/PID scheme) must live under $ZSH_HISTORY_DIR.

Two supported patterns:

  1. Single HISTFILE under the directory (simple):
    export HISTFILE="$ZSH_HISTORY_DIR/.zsh_history_$(date +%Y%m%d_%H%M%S)_$$"

  2. Split model (current maintainer / advanced setup):

    • export HISTFILE="$HOME/.zsh_history" — main zsh history for interactive recall.
    • export SESSION_HISTFILE="$ZSH_HISTORY_DIR/.zsh_history_$(date +%Y%m%d_%H%M%S)_$$" — session sidecar for logging / sas.

Do not hardcode $HOME/.zsh_history_* for session blobs; always use $ZSH_HISTORY_DIR for those. See zshrc-latest-changes.md Change 3 for cleanup and sas globs.

4. Migration procedure (one-time)

Pre-flight

  1. Close extra terminals; keep one shell for migration (reduces open HISTFILEs).
  2. Backup: cp ~/.zshrc ~/.zshrc.backup.$(date +%Y%m%d_%H%M%S) (you likely already use dated backups).

Steps

  1. mkdir -p ~/.zsh/history
  2. mv ~/.zsh_history_*(N) ~/.zsh/history/
    • zsh: (N) nullglobs safely; in bash use shopt -s nullglob or a loop.
    • Leave the main ~/.zsh_history (if present) untouched unless you explicitly merge it into a policy (see §6).
  3. Edit ~/.zshrc: add ZSH_STATE_DIR / ZSH_HISTORY_DIR, mkdir -p, and new HISTFILE pattern under $ZSH_HISTORY_DIR.
  4. Update smart_cleanup_histories / cleanup_session_histories globs from "$HOME"/.zsh_history_*(.N) to "$ZSH_HISTORY_DIR"/.zsh_history_*(.N).
  5. Update sas, csh, si, or any function that greps / cats session histories to use "$ZSH_HISTORY_DIR".
  6. Move lock/marker files if they live next to history (optional): e.g. ~/.zsh_cleanup_lock → ~/.zsh/cleanup.lock (only if you want everything under .zsh/; otherwise leave locks in ~ to minimize churn).
  7. source ~/.zshrc or exec zsh; open a new Cursor terminal and confirm echo $HISTFILE points under ~/.zsh/history/.
  8. After a week with no issues, delete stragglers: any remaining ~/.zsh_history_* in $HOME root (should be none).

5. Repository documentation & scripts to update (gspace1)

These files currently describe or assume $HOME/.zsh_history_*:

File Change
docs/english/Developers/zsh/enhanced-zsh-system.md Session history + cleanup sections: use $ZSH_HISTORY_DIR, note Finder cleanliness.
docs/english/Developers/zsh/zshrc-latest-changes.md Replace glob examples with "$ZSH_HISTORY_DIR"/.zsh_history_*(.N); keep the “no find $HOME” rule.
docs/english/Developers/zsh/.cursor-shell-optimization.md If it references history paths, align.

In-repo shell tooling (if any script greps home-root history): search for zsh_history_ and update.

# From repo root
rg 'zsh_history_|HISTFILE' --glob '*.{zsh,sh,md}'

6. Optional follow-ups (out of scope for “directory only” but related)

7. Acceptance criteria

8. Rollback

  1. Revert HISTFILE and cleanup globs in ~/.zshrc to the previous pattern.
  2. mv ~/.zsh/history/.zsh_history_* ~/ (if you need old layout back).
  3. exec zsh in all terminals.

Summary: Storing session histories under ~/.zsh/history/ is safe and standard as long as HISTFILE, cleanup, and helper functions all use the same ZSH_HISTORY_DIR and you migrate existing files once. The screenshot pattern (many tiny files) is expected for per-session HISTFILE; the win is organization and maintainability, not automatic merging into one timeline unless you change history strategy separately.

9. Implementation in this repo (what we can automate)

Artifact Purpose
migrate-zsh-history-directory.sh One-time mv of ~/.zsh_history_* → ~/.zsh/history/ (executable in repo).
zsh-history-path.snippet.zsh Merge into ~/.zshrc: ZSH_HISTORY_DIR, mkdir, HISTFILE, comments for cleanup / sas / csh.

What still requires you on the Mac: edit ~/.zshrc — it is not in this monorepo unless you open it in the editor.

Suggested order: run the migrate script → add the snippet to .zshrc → replace cleanup glob + helper functions → exec zsh in each terminal.