Show the previous session's UUID in the transcript after clear

Dotfiles repository for managing shell and tool configurations across macOS and Linux systems using Nix + Home Manager with Flakes.
~/.config/# Install Nix (using Determinate Systems installer)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Clone the repository
git clone https://github.com/kokatsu/dotfiles.git ~/dotfiles
cd ~/dotfiles
# Build and activate Home Manager configuration
DOTFILES_DIR="$PWD" nix run home-manager -- switch --flake . --impure
# User environment (packages / dotfiles) on both Linux and macOS.
# Set this to the actual repository path on each PC.
DOTFILES_DIR="$HOME/dotfiles" home-manager switch --flake "$HOME/dotfiles" --impure
# macOS system settings + Homebrew (quit Chrome first: casks get upgraded in place)
sudo HOSTNAME=$(hostname -s) DOTFILES_DIR="$HOME/dotfiles" darwin-rebuild switch --flake "$HOME/dotfiles" --impure
nix flake update
deno run --allow-all scripts/update-deno-deps.ts --all
DOTFILES_DIR="$HOME/dotfiles" home-manager switch --flake "$HOME/dotfiles" --impure
nix develop # Enter development shell with every linter / formatter
just check # Format check, lint, typos, script tests, and flake evaluation
just fmt # Format everything (Nix, Lua, shell, TOML, YAML, JSON, TypeScript)
just # List all recipes
Lefthook runs a subset of these on commit and push (formatters, linters, gitleaks, commitlint); the script tests and flake evaluation only run through just check and CI.
.
โโโ flake.nix # Flake outputs and Home Manager/nix-darwin builders
โโโ flake.lock # Locked dependencies for reproducibility
โโโ justfile # Task runner: checks, formatters, tests
โโโ nix/
โ โโโ home/ # Home Manager modules (packages, files, programs, services)
โ โโโ darwin/ # macOS system and Homebrew configuration
โ โโโ overlays/ # Custom packages and upstream workarounds
โโโ bin/ # User scripts, linked to ~/.local/bin/scripts and on PATH
โโโ scripts/ # Test and helper scripts used by just and CI
โโโ karabiner-config/ # Karabiner-Elements rules written with karabiner.ts
โโโ .config/ # Dotfile sources linked by Home Manager
โโโ zsh/ # Zsh shell configuration
โโโ nvim/ # Neovim configuration
โโโ wezterm/ # WezTerm terminal configuration
โโโ claude/ # Claude Code settings, hooks, and skills
โโโ ... # Other tool configurations
Static application settings live in nix/home/programs/. Home Manager generates Git, Ghostty, ripgrep, Starship, Yazi, Biome, markdown-oxide, ov, Taplo, psql, Vim, Claude Code keybindings, and the Codex base configuration from Nix. Edit these modules, then run home-manager switch --flake . --impure to apply them.
Yazi plugins come from nixpkgs yaziPlugins where available; the remaining plugins and flavors are vendored under .config/yazi/. All are installed through programs.yazi; update them via nix flake update or in Git instead of using ya pkg. Yazi's init.lua and git-changes.sh also need home-manager switch --flake . --impure after editing to apply the changes. Lua, shell scripts, and repository tooling configurations remain under .config/.
Neovim's lazy.nvim, peek.nvim, blink.cmp, Pkl plugin, and Tree-sitter parsers are built by Nix and linked under ~/.local/share/nvim/nix-plugins/. Other Lua plugins still use lazy-lock.json and :Lazy. zeno.zsh uses fixed Deno/npm dependencies from the Nix store. Ruby LSP and its Rails/RuboCop gems are fixed in nix/ruby/ruby-lsp/; Neovim uses RUBY_LSP_COMMAND to avoid a user-installed gem taking precedence. Karabiner's managed Default profile is built by Nix, then merged into the writable JSON while retaining other profiles and GUI settings.
Renovate PRs regenerate Deno locks/hashes and Ruby gemsets in pr.yml.
Claude Code's settings.json keeps its writable repository link. Codex receives a writable copy of its generated configuration and preserves the existing local state sections during activation.
MIT. Third-party notices are listed in THIRD_PARTY_LICENSES.md.
hooks/register.ts 22 lines1import type { Register } from "claude-code";
2
3export const register: Register = (on) => {
4 // /clear reloads no module (no session.start fires for it), so the ending
5 // session's id waits here for the fresh session's SessionStart.
6 let previous: string | undefined;
7
8 on("session.end", (_$, e, next) => {
9 if (e.reason === "clear") previous = e.sessionId;
10 return next(e);
11 });
12
13 on("classic.SessionStart", { source: "clear" }, async ($, e, next) => {
14 const result = await next(e);
15 if (!e.agent_id && previous) {
16 $.ui.log(`Previous session UUID: ${previous}`);
17 previous = undefined;
18 }
19 return result;
20 });
21};
22