Repository navigation
Description
Version
vfox 1.0.13 (release binary) and main at cdf0747 (2026-11-02, RuntimeVersion is still 1.0.13), with the nodejs plugin 0.5.3 from the registry. The two builds print the same lines in everything below.
OS
Linux (Ubuntu 26.04.1 on WSL2), bash 5.3.9, in a scratch HOME started with env -i. I ran only bash on Linux and only the nodejs plugin. The code involved depends on neither the shell nor the OS, and other plugins that declare legacyFilenames go through the same ParseLegacyFile call, so I expect the same elsewhere, but I have not tried.
Describe the bug
A legacy version file such as .nvmrc or .node-version takes effect when a shell starts in its directory, but not when you cd into that directory from a running shell. And a shell that started in such a directory stays on the file's version when you cd out of it, instead of going back to the global one. A .vfox.toml has neither problem.
At every prompt in such a directory vfox env still calls the plugin's ParseLegacyFile, so vfox does learn what the file says (Found nodejs@18.20.8 below). That answer is not what decides whether the cached output can be reused, so vfox prints the output it cached for the previous directory.
Steps to reproduce
legacyVersionFile.enable is true and legacyVersionFile.strategy is specified, the defaults.
$ vfox add nodejs
$ vfox install nodejs@18.20.8
$ vfox install nodejs@26.13.0
$ vfox use -g nodejs@26.13.0
$ mkdir -p ~/demo/plain ~/demo/with-nvmrc ~/demo/with-toml
$ printf '18.20.8\n' > ~/demo/with-nvmrc/.nvmrc
$ printf '[tools]\nnodejs = "18.20.8"\n' > ~/demo/with-toml/.vfox.tomlGoing in: a bash that starts in plain, then cds into the directory with the .nvmrc. The directory with the .vfox.toml switches right away, for comparison:
$ cd ~/demo/plain
$ eval "$(vfox activate bash)"
$ node -v
v26.13.0
$ cd ../with-nvmrc
$ cat .nvmrc
18.20.8
$ node -v
v26.13.0
$ vfox --debug env -s bash 2>&1 | grep -o -E "Parsing legacy file [^ ]+|Found nodejs@[0-9.]+|Config changed: [a-z]+|Using cached output"
Parsing legacy file /home/me/demo/with-nvmrc/.nvmrc
Found nodejs@18.20.8
Config changed: false
Using cached output
$ cd ../with-toml
$ cat .vfox.toml
[tools]
nodejs = "18.20.8"
$ node -v
v18.20.8Going out: a bash that starts in the directory with the .nvmrc, then cds to plain:
$ cd ~/demo/with-nvmrc
$ eval "$(vfox activate bash)"
$ node -v
v18.20.8
$ cd ../plain
$ node -v
v18.20.8
$ vfox --debug env -s bash 2>&1 | grep -o -E "Parsing legacy file [^ ]+|Found nodejs@[0-9.]+|Config changed: [a-z]+|Using cached output"
Config changed: false
Using cached outputRoot cause
envFlag (cmd/commands/env.go#L147-L197) decides whether it can reuse the output it cached for this shell:
- It first calls
loadToolChain(cmd/commands/tool_resolution.go#L31-L52), which reads the config files and callsParseLegacyFilefor the current directory. A legacy result only goes into the in-memory project config. configPathsthen lists only the vfox config files that exist (L154-L164). A directory with only a.nvmrchas no project config file, so the project entry is empty on both sides of thecd.ConfigState.HasChanged(internal/env/state.go#L112-L164) compares that project path with the last one,PATHwith the cachedPATH, and the modification time of each tracked file with the stored one. In the transcripts none of the three differs (Config changed: false), andenvFlagprints the cached output (L189-L197).
So the version file, and the directory it is read from, are inputs of the output that the cache does not know about. AGENTS.md already states the principle for path.mode: the cached output is keyed by the PATH policy "because config.yaml is not among the files whose modification time is tracked; a setting that changes the PATH order has to be part of that key" (L113), and it says that loadToolChain "reads the version files, legacy ones included, for env, activate and doctor" (L90).
Expected behavior
After cd ../with-nvmrc, node -v prints v18.20.8, and after cd ../plain from there it prints v26.13.0 again, as the directory with the .vfox.toml does.
Possible fix
env runs at every prompt (AGENTS.md), so both ideas below are comparisons of values that envFlag already has:
- Keep the working directory in the state, the way
CachedPathPolicykeeps the PATH policy, and count a different directory as a change. I tried this on v1.0.13: 55 added lines with a test (ConfigState.DirChangedandSetDir, used inenvFlag). A shell that went into a.nvmrcdirectory, a.node-versiondirectory, a plain directory, two.vfox.tomldirectories and a plain directory again came out right at every step, going out included, also with plugin 0.5.3. It does not notice an edit of the version file in the directory the shell is already in. - Keep what
loadToolChainresolved (the tool and version pairs, legacy ones included) and count a different set as a change.envFlaghas it before the cache check, so it needs no list of files per plugin, and it should also catch an edit in place. I have not tried this one.
Activity
docs: take the vfox patch out of this pull request, and link the issues
fix: LTS lines from the index, UTF-16/BOM .nvmrc, .nvmrc after a cd; …
thelastbackspace commentedon Nov 5, 2026
I'd like to take this one. I'm going with your second idea: store the tools the project scope resolved to (legacy ones included) in the env state next to the path policy, and treat a different set as a change. That also covers editing the .nvmrc in place, which the directory-only check misses. I have it working with a regression test on the existing env command fixture.
fix: refresh the env cache on legacy file, install, link and config c…
fix: follow .nvmrc/.node-version both ways on vfox 1.1.0, drop the en…