My baseline setup for code-first projects: mise manages language runtimes (Python, Node) and CLI tools (prek, just), uv manages Python packages and the virtualenv, pnpm manages Node packages. Tool-per-concern, no overlap.
| Tool | Purpose |
|---|---|
| git | Version control, and the source of Git Bash (the shell used here) |
| scoop | Windows package manager (installs mise, gh) |
| mise | Manages language runtimes (Python, Node) and CLI tools (prek, just) |
| uv | Manages Python packages and the virtualenv |
| pnpm | Manages Node packages (frontend only) |
| gh | GitHub CLI — manage repos, PRs, and releases from the terminal |
Install in order — git and scoop are the base layer the rest depends on.
Version control, and the source of Git Bash — the shell the rest of this setup runs in. Install git first: scoop and the other tools assume you already have a working shell.
Install: Download and install from the Git website.
Windows package manager. Installs CLI tools (mise, gh) into your user directory without admin rights or PATH clutter, which keeps the setup self-contained and easy to reproduce on a fresh machine.
Install: https://scoop.sh. Run in a non-admin PowerShell window:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-ExpressionUseful commands:
scoop update --all # update all installed apps
scoop cleanup --all # remove old versions
scoop list # list installed apps
scoop status # show apps with updates availableManages language runtimes (Python, Node) and CLI tools (prek, just).
Install: https://mise.jdx.dev/installing-mise.html#windows-scoop.
scoop install miseUseful commands:
mise install # install everything pinned in mise.toml
mise use --pin tool@latest # pin a tool's latest version into mise.toml
mise ls --current # show active tool versions and their source
mise doctor # diagnose setup issues
mise upgrade # update installed tools within their pinned ranges
scoop update mise # update mise CLIDon't mix a package manager with mise self-update. Since mise is installed via scoop, update
it with scoop update mise, not mise self-update. Running both leaves two divergent installs
that fight over the same location and report confusing version mismatches.
mise must be activated in the shell (not just on PATH via shims) for environment variables and
virtualenv auto-activation to work. Add this line at the bottom of your ~/.bashrc:
eval "$(mise activate bash)"So mise auto-activates the .venv that uv creates:
mise settings set python.uv_venv_auto sourcesource activates an existing .venv (uv owns creation). It writes to the global config at
~/.config/mise/config.toml.
Manages Python packages and the project venv.
Install: https://docs.astral.sh/uv/getting-started/installation/
Use uv's own installer, not a package manager. uv has first-class self-management (uv self update) and ships features frequently. A package manager only adds lag and re-creates the self-update conflict described under mise.
Useful commands:
uv sync # install/sync dependencies into .venv
uv add <package> # add a dependency
uv remove <package> # remove a dependency
uv run <command> # run a command in the project venv
uv check # type-check (runs ty)
uv self update # update uv itselfGitHub CLI — create and manage repos, PRs, and releases from the terminal.
Install: https://github.com/cli/cli/blob/trunk/docs/install_windows.md#scoop
scoop install ghUseful commands:
gh auth login # authenticate with GitHub
gh repo create # create a new repo
gh pr create # open a pull request
scoop update gh # update gh CLIOpen a fresh shell, then:
mise doctor # shell detected, no PATH warning, single mise version
mise --version
uv --versionmise doctor should report the correct shell (not (unknown)), no "tool paths are not first in
PATH" warning, and only one mise install.