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
- Per-session history files are created as
~/.zsh_history_YYYYMMDD_HHMMSS_<pid>(or similar) directly under$HOME. - Finder and
ls -a ~show dozens or thousands of dotfiles, which is noisy and easy to misread as “something broken.” - Repo docs (
enhanced-zsh-system.md,zshrc-latest-changes.md) and cleanup logic assume the pattern$HOME/.zsh_history_*in the home root; that assumption becomes obsolete after migration.
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:
- Define a single base directory (recommended below).
- Create the directory before any zsh sets
HISTFILE(first line of history-related block in.zshrc, ormkdir -pguard). - Point
HISTFILEat the new path for new shells only after you are ready. - Migrate existing files (optional but recommended) so old history is not orphaned.
- 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:
Single
HISTFILEunder the directory (simple):export HISTFILE="$ZSH_HISTORY_DIR/.zsh_history_$(date +%Y%m%d_%H%M%S)_$$"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
- Close extra terminals; keep one shell for migration (reduces open
HISTFILEs). - Backup:
cp ~/.zshrc ~/.zshrc.backup.$(date +%Y%m%d_%H%M%S)(you likely already use dated backups).
Steps
mkdir -p ~/.zsh/historymv ~/.zsh_history_*(N) ~/.zsh/history/- zsh:
(N)nullglobs safely; in bash useshopt -s nullglobor a loop. - Leave the main
~/.zsh_history(if present) untouched unless you explicitly merge it into a policy (see §6).
- zsh:
- Edit
~/.zshrc: addZSH_STATE_DIR/ZSH_HISTORY_DIR,mkdir -p, and newHISTFILEpattern under$ZSH_HISTORY_DIR. - Update
smart_cleanup_histories/cleanup_session_historiesglobs from"$HOME"/.zsh_history_*(.N)to"$ZSH_HISTORY_DIR"/.zsh_history_*(.N). - Update
sas,csh,si, or any function thatgreps /cats session histories to use"$ZSH_HISTORY_DIR". - 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). source ~/.zshrcorexec zsh; open a new Cursor terminal and confirmecho $HISTFILEpoints under~/.zsh/history/.- After a week with no issues, delete stragglers: any remaining
~/.zsh_history_*in$HOMEroot (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)
- Merge policy: If the goal is “smarter arrows,” consider one shared
HISTFILEplus zsh options (SHARE_HISTORY,INC_APPEND_HISTORY,HIST_IGNORE_DUPS) vs many session files — that is a product decision (privacy/isolation vs unifiedfc -l). This spec does not require changing that model. - Arrow intelligence: See prior review: fix
precmd-only index reset, ranking (recency vsuniq -c), regex safety — track as a separate task. - Atuin / McFly: Optional replacement layer for search/ranking; history file location remains relevant for native zsh fallback.
7. Acceptance criteria
- New shells create history files only under
~/.zsh/history/(or chosenZSH_HISTORY_DIR). - No new
~/.zsh_history_*files appear in$HOMEroot after migration. - Daily cleanup still runs, uses one directory glob, no recursive
find $HOME. -
sas/csh/ similar still work against migrated files. - Docs in
docs/english/Developers/zsh/reflect the new paths and env vars.
8. Rollback
- Revert
HISTFILEand cleanup globs in~/.zshrcto the previous pattern. mv ~/.zsh/history/.zsh_history_* ~/(if you need old layout back).exec zshin 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.