Bootstrap scripts for development environments on MacBooks and Cloud servers.
Tested on macOS 15.7.2
softwareupdate --install --all- Brave Browser
- Raycast
- Flycut (Clipboard manager)
- Zoom (Meeting)
- Canva (Designer App)
- NTFS for Mac - Paragon Software
- NordVPN
- Zed Code Editor
- Visual Studio Code
- iTerm2
- Docker Desktop
- iCloud
- Turn off syncing of Photos, iCloud Drive, Messages etc. and only enable syncing for Passwords and Notes
- Spotlight (replaced with Raycast)
- Uncheck "Show Spotlight in the menu bar"
- Uncheck unneeded Spotlight search categories
- Disable Spotlight keyboard shortcuts
- In System Settings, go to Spotlight, click Search Privacy in the lower right, then add the disk you want to exclude from Spotlight indexing
- Adjust "Lock Screen" Settings
- Turn off Siri
$ mdutil
Usage: mdutil -pEsa -i (on|off) -d volume ...
mdutil -t {volume-path | deviceid} fileid
Utility to manage Spotlight indexes.
-i (on|off) Turn indexing on or off.
-d Disable Spotlight activity for volume (re-enable using -i on).
-E Erase and rebuild index.
-s Print indexing status.
-a Apply command to all stores on all volumes.
-t Resolve files from file id with an optional volume path or device id.
-p Publish metadata.
-V vol Apply command to all stores on the specified volume.
-v Display verbose information.
-r plugins Ask the server to reimport files for UTIs claimed by the listed plugin.
-L volume-path List the directory contents of the Spotlight index on the specified volume.
-P volume-path Dump the VolumeConfiguration.plist for the specified volume.
-X volume-path Remove the Spotlight index directory on the specified volume. Does not disable indexing.
Spotlight will reevaluate volume when it is unmounted and remounted, the
machine is rebooted, or an explicit index command such as 'mdutil -i' or 'mdutil -E' is
run for the volume.
NOTE: Run as owner for network homes, otherwise run as root.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"Enter passphrase when prompted.
ssh-keygen -t ed25519 -C 'my-ssh-key'Also, see Generating SSH keys
ssh-add is a command that adds SSH private keys to the ssh-agent, which is a background program that manages your SSH keys and remembers your passphrases.
Use ssh-add to add your SSH private key to the ssh-agent and store your passphrase in the keychain.
ssh-add --apple-use-keychain ~/.ssh/id_ed25519Add the following to ~/.ssh/config to use the specific SSH key for GitHub.
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
pbcopy < ~/.ssh/id_ed25519.pubAdd SSH key to GitHub
# Change to user home directory and clone under ~/bootstrap
cd ~
git clone git@github.com:ninegene/bootstrap.git
cd bootstrap/macOS
./install-homebrew.sh
./install-xcode-command-line-tools.sh
./disable-spotlight-indexing.sh
./install-aws-cli.sh
./install-search-and-navigation-tools.sh
./install-github-cli.sh
./install-gnu-packages.sh
./install-go.sh
./install-llama-cpp.sh
./install-ruby.sh
./install-opencode-cli.sh
./install-crush.sh
./install-antigravity-cli.sh
./install-claude-code.sh
./install-ai-agent-instructions.sh
./install-nvm.sh
./install-postgresql-18.sh
./install-elixir.sh
./install-shellcheck-and-shfmt.sh
./install-zsh-git-prompt.sh
./configure-iterm.sh
./configure-git.sh
./configure-vim.sh
./configure-zsh.sh
brew doctor
brew cleanup -sThe user-level AI instruction template lives in macOS/ai-agent-instructions.md. The installer symlinks it to the supported global locations for each AI tool:
| Tool | Global instruction path |
|---|---|
| GitHub Copilot | ~/.config/github-copilot/global-copilot-instructions.md |
| GitHub Copilot (alt) | ~/.copilot/instructions/global.instructions.md |
| OpenCode | ~/.config/opencode/AGENTS.md |
| Codex | ~/.codex/AGENTS.md |
| Claude Code | ~/.claude/CLAUDE.md |
| Gemini CLI | ~/.gemini/GEMINI.md |
Reusable agent skills live in skills/. Each skill is a directory containing a SKILL.md file with YAML frontmatter:
skills/
└── my-skill/
└── SKILL.md
Minimal SKILL.md format (compatible across all tools):
---
name: my-skill
description: What it does and when the agent should invoke it.
---
Instructions for the agent...Run ./install-ai-agent-skills.sh to symlink the skills folder to all supported tools:
| Tool | Skills path | Notes |
|---|---|---|
| Claude Code | ~/.claude/skills/<skill>/ |
per-skill dir symlinks (dir-level not followed) |
| GitHub Copilot | ~/.copilot/skills/ |
also reads ~/.claude/skills/ |
| OpenAI Codex | ~/.codex/skills/ |
whole dir symlink |
| OpenCode | ~/.config/opencode/skill/ |
whole dir symlink (singular skill/) |
| Antigravity CLI | ~/.gemini/antigravity-cli/plugins/user-skills/skills/ |
plugin-based; plugin.json created automatically |
Adding a new skill to skills/ automatically makes it available to all tools that support whole-dir symlinks. For Claude Code, re-run the installer to pick up new skill dirs.
This repo uses Lefthook to run checks on staged files before each commit.
Lefthook is included as a step in bootstrap.sh. To install it standalone:
brew install lefthook
lefthook installDefined in lefthook.yml, these run in parallel on git commit:
| Hook | Files | What it checks |
|---|---|---|
shellcheck |
*.sh |
Shell script issues (severity: warning+) |
shfmt |
*.sh |
Formatting — 4-space indent, consistent indentation |
markdownlint |
*.md |
Markdown style and structure |
# Run all pre-commit hooks manually against staged files
lefthook run pre-commit
# Run a single hook by name
lefthook run pre-commit --commands shellcheck
# Skip hooks for a one-off commit
LEFTHOOK=0 git commit -m "message"
# Re-install hooks after pulling changes to lefthook.yml
lefthook install