Issue details
If ~/.zshenv sets ZDOTDIR without export, fx never notices edits to $ZDOTDIR/.zshrc. It keeps replaying the snapshot it captured at startup, so changes made through fx's own shell tool don't show up in later commands. ${ZDOTDIR} is also unset inside the commands fx runs, even though a real login zsh has it set
This is separate from the known limitation in #1329 (it covers edits to a file your startup files source. Here $ZDOTDIR/.zshrc is a top-level startup file that zsh reads directly and it still isn't watched
Repro
Needs zsh, since print -r is a zsh builtin.
d=$(mktemp -d); mkdir $d/zd
print -r -- "unset ZDOTDIR; ZDOTDIR=$d/zd" > $d/.zshenv
print -r -- 'export MARK=v1' > $d/zd/.zshrc
cd $d
# zsh itself handles the unexported form fine:
ZDOTDIR=$d zsh -lic 'echo "param=$ZDOTDIR mark=$MARK"'
# => param=/tmp/.../zd mark=v1
ZDOTDIR=$d fx ask --auto --no-save --json "Run these as three separate shell tool calls. Wait for each result before making the next call. Call 1: echo \$MARK Call 2: echo 'export MARK=v2' > $d/zd/.zshrc Call 3: echo \$MARK"Call 3 should print v2. On fx 0.0.15 (Ubuntu 24.04, zsh 5.9, gpt-6-luna):
Running echo $MARK
v1
Running echo 'export MARK=v2' > ./zd/.zshrc
Running echo $MARK
v1The model rewrote call 2 to a relative path, but cwd is $d so it writes to the same file.
The same run in a fresh temp dir with export ZDOTDIR=$d/zd prints v2 on call 3. The export is the only difference between the two runs.
Cause
The capture script collects variables with env, which only prints exported ones (src/core/terminal/shell_resolver.zig:331):
\\/usr/bin/env -0ZDOTDIR is picked up from that list (src/core/terminal/shell_snapshot.zig:708):
if (std.mem.eql(u8, key, "ZDOTDIR") and value.len != 0) {
generation.zdotdir = try alloc.dupe(u8, value);
}and that value decides which directories get watched (src/core/terminal/shell_snapshot.zig:658):
const dirs = [_]?[]const u8{ io_mod.getenv("ZDOTDIR"), io_mod.homeDir(), snapshot_zdotdir };An unexported ZDOTDIR shows up in neither env -0 nor getenv, so the directory holding the startup files never makes it into that list. Both lines came in with #1137.
Does anyone actually skip the export?
ZDOTDIR is a shell parameter, so zsh reads $ZDOTDIR/.zshrc either way. Kitty's zsh integration carries a comment saying it normally shouldn't be exported, and has code for the case where it is. Ghostty's version started as a copy of Kitty's. Most dotfiles I looked at do export it, so this setup is a minority, but it works fine under zsh.
Possible fix
Emit ZDOTDIR explicitly from the capture script instead of relying on env. Happy to put up a PR but would love to know if you'd prefer that narrow fix or something more general for shell parameters.
Tested on commit d72b46594ca6 (fx 0.0.15), no local changes
Sensitive information
- I reviewed the information above and removed secrets and other sensitive data.
Activity
0No activity yet.