uv is its own thing, but direnv + `source .venv/bin/activate` is straightforward nowadays.
Direnv has saved me so much pain nowadays
I like direnv too. But if you're not planning to use uv, you might want to give pipenv a try - the officially recommended tool for the purpose. Pipenv has just one command to create a virtual environment and install packages. And while pipenv can handle traditional requirements.text file, it's real strength is pipfile - a richer format with supporting lock files.
Pipenv doesn't automatically activate the venv on entry into a shell. But a shell plugin named pipenv-activate supports this. It does what you use direnv for in this case, without an envrc file in the source.
One major difference of pipenv from vanilla venv is that pipenv creates the venv in a common location outside the project (like poetry does). But this shouldn't be a big problem, since you wouldn't commit the venv into VCS anyway.
Why `source .venv/bin/activate` with direnv? I use `layout python` in my .envrc and direnv activates the venv on entry to the directory.