One-command developer environment setup for Linux, macOS, WSL, Windows, and GitHub Codespaces.
| Tool | Purpose |
|---|---|
zsh |
Shell -- installed and set as default on Linux/macOS |
uv |
Python package and environment manager (fast pip replacement) |
nvm + Node.js LTS |
Node Version Manager + latest Node LTS |
gh |
GitHub CLI |
| GitHub Copilot CLI | AI pair programmer in your terminal (gh copilot) |
squad CLI |
Multi-agent copilot orchestrator (gosquad launcher) |
vim |
Modal text editor -- installed on all platforms |
tmux |
Terminal multiplexer (Linux/macOS) |
psmux |
Terminal multiplexer (Windows) |
| Shell aliases | Shortcuts for common git and dev commands |
| Platform | Status | Entry point |
|---|---|---|
| Linux (native) | [x] | bash setup.sh |
| macOS | [x] | bash setup.sh |
| WSL (Windows Subsystem for Linux) | [x] | bash setup.sh |
| Windows (native PowerShell) | [x] | powershell -ExecutionPolicy Bypass -File setup.ps1 |
| Dev Container / GitHub Codespace | [x] | Automatic (runs on container creation) |
git clone https://github.com/primetimetank21/dev-setup.git
cd dev-setup
bash setup.shGitHub Authentication: During setup,
scripts/linux/tools/auth.sh(Linux/macOS/WSL) orscripts/windows/tools/auth.ps1(Windows) checks whether you are already authenticated with the GitHub CLI. If not, it launchesgh auth logininteractively so you can complete authentication. In non-interactive environments (CI, GitHub Codespaces, piped stdin), the prompt is skipped automatically and a warning is printed -- rungh auth loginmanually after setup completes if you need to authenticate.
git clone https://github.com/primetimetank21/dev-setup.git
cd dev-setup
powershell -ExecutionPolicy Bypass -File setup.ps1No action needed. Setup runs automatically on container creation via the postCreateCommand hook.
By default, setup.sh / setup.ps1 installs all tools in the defined order. Use flags to control
which tools run without editing the scripts.
| Flag | Linux / macOS / WSL | Windows |
|---|---|---|
| List available tools, exit (no install) | --list |
-List |
| Print usage, exit | --help |
-Help |
| Install only the named tools | --only=a,b,c |
-Only 'a,b,c' |
| Install all defaults except named tools | --skip=a,b,c |
-Skip 'a,b,c' |
# List all selectable tool names
./setup.sh --list
# Print usage
./setup.sh --help
# Install only zsh and uv
./setup.sh --only=zsh,uv
# Install everything except auth and copilot-cli
./setup.sh --skip=auth,copilot-cli# List all selectable tool names
.\setup.ps1 -List
# Print usage
.\setup.ps1 -Help
# Install only vim and gh
.\setup.ps1 -Only 'vim,gh'
# Install everything except auth and copilot-cli
.\setup.ps1 -Skip 'auth,copilot-cli'--only / -Only does not auto-include prerequisites. Selecting --only=nvm installs
only nvm -- its prerequisite tools (prereqs, zsh, uv) are not pulled in automatically.
If a tool depends on earlier steps, list all required tools explicitly:
# Correct: include prereqs before nvm
./setup.sh --only=prereqs,uv,nvm--only and --skip are mutually exclusive. Combining both flags exits with an error.
--list takes precedence. If --list is passed alongside --only or --skip, the tool
list is printed and setup exits without installing anything.
Tool names are validated against the available list. An unknown name exits with an error and
prints the available tools. Use --list / -List to see valid names before running.
After running setup, complete these steps to activate your tools:
-
Activate Node.js:
nvm install --lts nvm use --lts
-
Dotfiles: Your shell config (
.zshrc/.bashrc) and aliases are automatically installed during setup. Restart your terminal or runsource ~/.zshrc(orsource ~/.bashrcfor bash) to load them.
-
Activate Node.js: Open a new terminal and run:
nvm install lts nvm use lts
-
Aliases: PowerShell aliases are automatically added to your profile. Restart your terminal to load them.
dev-setup/
+-- setup.sh -- Entry point for Linux / macOS / WSL
+-- setup.ps1 -- Entry point for Windows (PowerShell)
+-- ARCHITECTURE.md -- Technical architecture and team ownership map
+-- CHANGELOG.md -- Release history (Keep a Changelog format)
+-- CONTRIBUTING.md -- Contribution guide
+-- scripts/
| +-- lib/
| | +-- read-tool-version.sh -- POSIX sh: reads pinned version from .tool-versions
| | +-- Read-ToolVersion.ps1 -- PowerShell: Get-ToolVersion function
| | \-- ascii-sweep.py -- Sweep .md files for non-ASCII; preserves fenced code blocks (#322A)
| +-- linux/
| | +-- setup.sh -- Core Linux/macOS installer (orchestrates tool scripts)
| | \-- tools/ -- Individual tool install scripts
| | +-- auth.sh -- GitHub CLI authentication (interactive)
| | +-- copilot-cli.sh
| | +-- squad-cli.sh
| +-- gh.sh
| | +-- nvm.sh
| | +-- uv.sh
| | \-- zsh.sh
| \-- windows/
| +-- setup.ps1 -- Orchestrator (dot-sources tools/ scripts)
| +-- uninstall.ps1 -- Idempotent reverse of the installer
| \-- tools/ -- Per-tool install scripts
| +-- auth.ps1 -- GitHub CLI authentication (interactive)
| +-- copilot.ps1, dotfiles.ps1, gh.ps1, git.ps1, nvm.ps1
| +-- profile.ps1, psmux.ps1, squad-cli.ps1
| +-- uv.ps1, vim.ps1
| \-- (10 files total)
+-- config/
| \-- dotfiles/ -- Dotfile templates (.aliases, .gitconfig, .editorconfig, etc.)
| \-- install.sh -- Dotfile installer
+-- hooks/
| +-- commit-msg -- Enforce Conventional Commits
| +-- pre-commit -- 4-check hygiene gate (branch ancestry + ASCII on .ps1/.md/.sh + branch refusal + shellcheck); see Git Hooks below
| +-- prepare-commit-msg -- Rewrite merge/revert messages to Conventional Commits form; see Git Hooks below
| \-- pre-push -- Block pushes to main; advisory linting
+-- tests/ -- Validation tests (bash + PowerShell)
| +-- test_aliases.sh
| +-- test_git_hooks.ps1
| +-- test_idempotency.sh
| +-- test_remove_custom_item.ps1
| \-- test_windows_setup.ps1
+-- .devcontainer/ -- Dev Container / Codespace configuration
+-- .github/workflows/ -- CI validation and automation
Root entry points (setup.sh, setup.ps1) are thin routers -- they detect the OS and delegate to the appropriate script under scripts/. They install nothing themselves.
After running setup, you get shortcuts for common git, dev, and utility commands. All aliases work on both Linux/macOS and Windows.
Git shortcuts: ga, gaa, gc, gcm, gcb, gco, gd, gds, gf, gfp, ggs, ggsls, ggsp, gl, glog, gp, gpf, gpl, grb, grbi, grs, grss, gs
GitHub CLI: ghpr, ghprl, ghprv, ghis, ghiv
Dev tools: uvr, uvs, ni, nr, nrd, nrt, py, c, gosquad
Utility: myip, pb, h, ep
tmux/psmux: ta (attach), tt (new session), tls (list sessions), tks (kill server)
Navigation (Linux/macOS only): .., ..., ...., ~, -
ls shortcuts (Linux/macOS only): ll, la, l, lh
Full definitions:
- Linux/macOS:
config/dotfiles/.aliases(bash/zsh only -- see header for the non-POSIX features in use) - Windows:
scripts/windows/setup.ps1(theWrite-PowerShellProfilefunction)
Three helper functions are available after setup:
create_tmux -- Create or attach to the tank_dev tmux session. If the session exists, attaches to it. If not, creates it first.
create_tmuxstart_up -- Shortcut that calls create_tmux. Use this in your shell startup for auto-attach.
start_upNew-PsmuxSession -- Create or attach to the tank_dev psmux session (Windows equivalent of create_tmux).
New-PsmuxSessionBoth setup.sh and setup.ps1 automatically configure git to use this repo's hooks directory:
git config core.hooksPath hooksNo manual copying needed. After running setup, four hooks are active:
Six ordered hygiene checks (fastest-first); the commit is blocked if any check fails:
- Branch ancestry -- feature branches must descend from
develop. Catches accidental forks-of-forks. - ASCII-only content on staged
.ps1,.md, and.shfiles. PS 5.1 on Windows uses CP1252, so non-ASCII bytes (em dashes, smart quotes, curly apostrophes) break string literals; Markdown and shell sources should also stay ASCII-clean for portablegrep/sed/diff. If a.mdfile trips this check, runpython scripts/lib/ascii-sweep.py --dry-runto preview fixes, then drop--dry-runto apply (see below). The sweep preserves fenced code blocks verbatim, so any non-ASCII inside...must be cleaned by hand. - Refuse direct commits on
develop/main/master-- create a feature branch first. - Shellcheck on staged
.shfiles. Silently skipped ifshellcheckis not installed.
Enforces Conventional Commits format:
type(scope): description
Valid types: feat, fix, docs, style, refactor, test, chore, ci, build, perf, revert. Hard reject on non-conforming messages.
Rewrites git's auto-generated merge/revert commit messages into Conventional Commits form so commit-msg can validate them (e.g. Merge pull request #N from USER/BRANCH -> merge(pr): #N from USER/BRANCH, Revert "SUBJECT" -> revert: SUBJECT). Non-matching messages are left unchanged.
- Blocks direct pushes to
main(hard stop). - Runs shellcheck on changed
.shfiles -- advisory, never blocks (Linux/macOS). - Runs PSScriptAnalyzer on changed
.ps1files -- advisory, never blocks (requirespwsh+ PSScriptAnalyzer module; silently skipped if absent).
Use --no-verify to bypass hooks in emergencies.
Auto-replaces common non-ASCII characters in repo source files -- em/en dashes, smart quotes, ellipsis, arrows, box-drawing glyphs, status emoji -- with ASCII equivalents. The usual trigger is pre-commit Check 2 rejecting a commit because a Markdown file picked up Unicode punctuation from a paste.
python scripts/lib/ascii-sweep.py --dry-run # preview replacements
python scripts/lib/ascii-sweep.py # apply in-placeWhen NOT to use it: the sweep deliberately preserves fenced code blocks (...) verbatim, so it will not touch non-ASCII inside code fences. The pre-commit ASCII check does NOT respect fence boundaries -- if your non-ASCII bytes are inside a code fence (e.g. a file-tree drawn with box-drawing glyphs), the sweep will report them as "remaining" and you must hand-convert. Added in Sprint 13 (#322A / PR #335).
Tool versions are pinned in .tool-versions at the repo root (asdf/mise format). This file is the single source of truth for every tool version installed by setup, including nvm-windows. Setup scripts read it directly via scripts/lib/read-tool-version.sh (POSIX) and scripts/lib/Read-ToolVersion.ps1 (Get-ToolVersion) -- no asdf or mise dependency needed.
nodejs 22.11.0
nvm 0.39.7
nvm-windows 1.2.2
uv 0.4.18
copilot-cli 1.0.48
gh 2.92.0
To bump a tool version, edit the version number in .tool-versions and re-run setup. Each line is toolname version, one per line. Blank lines and lines starting with # are ignored.
Dotfiles: Edit or add templates in config/dotfiles/. Each file is copied into your home directory on first run. Existing files are not overwritten unless you pass --force.
Adding a tool: Scripts must be idempotent -- check whether the tool is already installed before doing anything.
- Linux: Drop
scripts/linux/tools/<name>.sh. Add"<name>"to theDEFAULT_TOOLSarray inscripts/linux/setup.shat the desired position to include it in the default run. A script present intools/but absent fromDEFAULT_TOOLSis available (selectable later via--only) but not installed by default. - Windows: Drop
scripts/windows/tools/<name>.ps1defining anInstall-<Name>function. Register it in$ToolRegistry('<name>' = { Install-<Name> }) and add'<name>'to$DefaultTools, both inscripts/windows/setup.ps1.
When a sprint wraps, every issue and PR carrying its sprint:N label needs the same end-state transition:
- remove
release:backlog(if present) - add
release:shipped-X.Y.Z(if missing)
Type, area, and priority labels are never touched.
This is done by scripts/sprint-end-labels.sh and the matching workflow .github/workflows/sprint-end-labels.yml.
Manual / local run (dry-run by default for safety):
scripts/sprint-end-labels.sh \
--sprint sprint:17 \
--release-label release:shipped-1.17.0 \
--dry-runRemove --dry-run to apply changes.
Workflow trigger: Actions tab -> "Sprint End Labels" -> Run workflow. Inputs: sprint_label, release_label, dry_run (defaults to true).
Verification (HARD REQUIREMENT): After every label op, the script re-queries the issue and asserts the desired state. On mismatch it retries the read (not the write) with exponential backoff -- 1s, 2s, 4s -- then fails loudly. The CLI's exit code alone is treated as necessary-but-not-sufficient.
Idempotent: Safe to run twice. A second run finds no work to do.
Tested by: tests/test_sprint_end_labels.ps1 -- six scenarios including a function-override harness that exercises both the retry-and-succeed and fail-loudly-after-3 paths without hitting the GitHub API.
See CONTRIBUTING.md for the full contributor workflow: branch naming (feat|fix|chore|docs|refactor/{issue}-{slug} from develop), PR checklist, Conventional Commits format, test harness pattern.
All commits follow Conventional Commits format: type(scope): description. Valid types: feat, fix, docs, style, refactor, test, chore, ci, build, perf, revert. The commit-msg hook enforces this; prepare-commit-msg rewrite auto-converts merge/revert messages to Conventional Commits form.
PRs to develop use regular merge commits (not squash) for full history preservation. Release cuts from develop to main also use regular merges.
- ARCHITECTURE.md -- full technical overview, OS detection logic, script conventions, Windows dependency order, and a guide for adding a new tool.
- CONTRIBUTING.md -- contributor workflow, branch naming, PR checklist, test harness pattern.
- CHANGELOG.md -- release history in Keep a Changelog format.
For deeper technical detail -- OS detection logic, script conventions, decision records -- see ARCHITECTURE.md.