prepare is the only lifecycle script that runs in three distinct situations: before npm pack and npm publish, on a plain npm install with no arguments in the package directory, and when the package is installed as a git dependency such as github:org/repo#branch.
That third case is the useful one and the surprising one. Installing a TypeScript library straight from git works only if prepare builds it, because the registry tarball's dist is not in the repository. It is also a foot-gun: any dev-time tooling in prepare, such as husky install, will run in consumers' CI when they install from git, and will fail there because devDependencies of a git dependency are installed but the environment may lack a .git directory.
Use prepare for build steps that a git consumer needs. Use prepublishOnly for checks that should run only when publishing. Guard developer-only setup with a condition, since npm 7 and later run prepare for git installs in production installs too.