A project could run bench for months without a manager/local/.env:
everything fell back to core/.env.example, so the two settings that
change what bench is — claim-on-move and syncing through origin/main —
were invisible to anyone who had not read that file.
install.py now writes it on a first run. It asks three questions no
default can be right about (solo or team, which agent adapter, the
project's test command) and writes core/.env.example with the answers
substituted into their lines, comments and all keys intact — so the
written file is where the project reads what else it can change.
- Runs after first_boot_clean: .env is one of the two things the
first-boot guard reads, so writing it earlier would skip the clean.
- Never asks without a terminal on stdin. install.py sits on the path
of start.sh, update.sh and every hook, so no TTY prints one line and
carries on rather than blocking a board start on an invisible prompt.
--dry-run reports the questions and writes nothing.
- An existing .env is never touched; --setup is the only way back to
the questions, pre-filling from the current file and rewriting it in
place, so start.sh's fallback BOARD_PORT line survives.
- Bare Enter takes every default (the result is the example verbatim,
i.e. today's behaviour exactly); Ctrl-D skips the rest.
start.sh needed no change — it already calls install.py before the port
dance, which is the right order. Tests drive the interactive runs over a
real pty and the non-interactive ones with /dev/null on stdin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>