Jump to a section
A Mac setup you can reason about.
Know which tools run, where they live, and what they are allowed to change. Follow the setup path, then use the command toolkit when something fails.
Know your Mac and its toolchain
Start with the operating system and the architecture of the current process. Apple Silicon and Intel need different native binaries. A translated terminal on Apple Silicon may report x86_64, so uname alone does not identify the physical CPU.
sw_vers
uname -m
# Reports 1 for a translated process when this sysctl key is available
sysctl -in sysctl.proc_translated 2>/dev/null
# Hardware capability signal; availability varies by machine
sysctl -n hw.optional.arm64 2>/dev/nullAn absent sysctl value is not proof of a particular CPU. Check About This Mac if the result is unclear. Prefer native arm64 tools on Apple Silicon. Inspect a known binary with file; scripts and symlinks may need you to inspect their interpreter or resolved target too.
command -v brew
command -v node
file /bin/zshReference: Homebrew installation requirements. Documentation checked 5 September 2026.
Rosetta runs Intel apps on Apple Silicon where supported. Use it only for a known Intel-only dependency, keeping that toolchain separate. Apple says broad Rosetta availability continues through macOS 27, with restricted uses starting in macOS 28. Prefer native or Universal updates instead of making Rosetta a permanent prerequisite.
Reference: Apple: Intel-based apps and Rosetta. Documentation checked 5 September 2026.
Install and verify Xcode Command Line Tools
Command Line Tools provide compilers, SDK components and developer utilities used by native dependencies. Full Xcode is needed for many Apple-platform app workflows, but a terminal-only project may need just CLT.
xcode-select --installComplete the system dialog, then verify the active developer directory and compiler. A full Xcode selection may resolve inside Xcode.app rather than /Library/Developer/CommandLineTools; both can be legitimate.
xcode-select -p
xcrun --find clang
clang --versionIf a macOS update leaves an invalid developer path or missing SDK, check the selected installation and available Apple updates. Do not remove the developer directory as a first response. Change the active selection only after confirming the intended Xcode or CLT installation exists.
Reference: Apple: Xcode and Command Line Tools resources. Documentation checked 5 September 2026.
Install Homebrew and establish one PATH
If command -v brew fails, Homebrew may be missing or simply absent from PATH. Inspect its expected location before reinstalling. The conventional native prefix is /opt/homebrew on Apple Silicon and /usr/local on Intel; translated Intel installations can coexist, which requires deliberate separation.
command -v brew
ls -ld /opt/homebrew /usr/local/Homebrew 2>/dev/nullUse the installation instructions on brew.sh. Read the script and its prompts before running it. Installation changes system directories and may request an administrator password; routine brew package operations should not be run with sudo.
# Download for inspection; this creates a local file
curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh -o brew-install.sh
less brew-install.sh
# After reviewing the downloaded installer
/bin/bash brew-install.shFollow the installer’s printed “Next steps” for your actual shell. For a conventional native installation, add the matching line once to ~/.zprofile. Do not paste both lines or repeatedly append duplicates.
# Apple Silicon native installation: ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Intel installation: use this line instead
eval "$(/usr/local/bin/brew shellenv)"brew --version
brew --prefix
brew config
brew doctorReview doctor warnings in context. brew update refreshes package metadata; brew outdated inspects available upgrades; brew upgrade changes installed packages. Coordinate runtime upgrades with project version constraints.
brew update
brew install git
# Inspect before choosing any upgrade
brew outdatedReference: Homebrew installation and shell setup. Documentation checked 5 September 2026.
Reference: Homebrew prefixes and package management. Documentation checked 5 September 2026.
Understand Terminal, zsh and shell startup
Terminal is the application; zsh is the command interpreter. $SHELL normally describes your configured login shell, not necessarily the process you are currently running. ~/.zprofile is read by login zsh, while ~/.zshrc is read by interactive zsh. With ZDOTDIR unset, these files are under $HOME.
printf '%s
' "$SHELL"
ps -p $$ -o comm=
printf '%s
' "$HOME"
printf '%s
' "$PATH"
command -v git
command -v codexKeep environment setup intentional. Put aliases and prompt customizations in .zshrc; follow installer guidance for login-shell PATH initialization. An editor terminal may be interactive but not a login shell. Restart the application after configuration changes and compare executable paths rather than assuming every terminal inherits the same environment.
# Shell configuration: add only if this directory is your chosen tool location
export PATH="$HOME/.local/bin:$PATH"
# Session environment example; not a secret
export APP_ENV=development
# One-command environment override
APP_ENV=test ./scripts/check.shexport makes a variable available to child processes. A script does not automatically inherit interactive aliases. State its interpreter with a shebang: #!/bin/sh for POSIX syntax or #!/bin/zsh for zsh features. macOS /bin/bash is not necessarily the modern Bash used by Linux scripts.
Quote "$HOME/Projects/My App". Unquoted globs expand before a command runs; zsh commonly reports “no matches found” when a glob has no matches. Quote a search pattern when the receiving tool should interpret it. A tilde inside quotes stays literal: "~/Projects" is not your home directory.
Reference: Zsh: startup files. Documentation checked 5 September 2026.
Configure Git, SSH and your editor
Verify Git from CLT or your chosen package manager, then set identity deliberately. Repository-local configuration is useful when work and personal projects use different identities. Global configuration affects future commands across repositories.
git --version
git config --show-origin --get user.name
git config --show-origin --get user.email# Replace these example identity values
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
git config --global init.defaultBranch mainFollow the repository’s .gitattributes for line endings. Avoid changing global settings to repair one noisy diff. A shared text=auto eol=lf policy can help new cross-platform projects; review its impact before normalizing existing files.
# Example .gitattributes content
* text=auto eol=lfIf using SSH, first inspect existing keys so you do not overwrite one. Create a dedicated key with a passphrase and a deliberate filename. Upload only its .pub file to your Git host. Never share a private key.
ls -la "$HOME/.ssh"
ssh -V# Creates a private key directory if needed
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
# Creates a new key; do not overwrite an existing path
ssh-keygen -t ed25519 -C "[email protected]" -f "$HOME/.ssh/id_ed25519_codac"
# Apple-provided ssh-add can store the passphrase in Keychain
/usr/bin/ssh-add --apple-use-keychain "$HOME/.ssh/id_ed25519_codac"Choose the key with an IdentityFile entry for the relevant host in ~/.ssh/config. Verify the host fingerprint against your provider’s published keys before trusting a first connection. A host’s SSH test may authenticate successfully but return a nonzero exit because it offers no shell.
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_codac
IdentitiesOnly yes
AddKeysToAgent yes
UseKeychain yesUseKeychain and --apple-use-keychain are Apple extensions; other OpenSSH distributions may reject them. For HTTPS, use the host’s supported credential helper instead of putting tokens in remote URLs.
Reference: GitHub: macOS SSH and Keychain. Documentation checked 5 September 2026.
VS Code is optional. Install the matching Mac build, open the project folder and use its Source Control view to review diffs. To enable code in Terminal, run “Shell Command: Install code command in PATH” from the Command Palette, then open a new terminal. Other editors work with the same CLI workflow.
Reference: VS Code on macOS. Documentation checked 5 September 2026.
Install, authenticate and verify Codex
Use the current official CLI installation route and select the package for your environment. The standalone installer does not require a project Node runtime. Download the installer for review before running it; do not combine competing installation methods without understanding PATH order.
curl -fsSL https://chatgpt.com/codex/install.sh -o codex-install.sh
less codex-install.sh
# Run only after reviewing the installer
sh codex-install.shcommand -v codex
codex --version
codex --helpIf the command is missing, follow the installer’s PATH instructions and reopen Terminal. Confirm which executable is selected before updating. For npm or Homebrew alternatives, use the installation tabs in the official documentation and verify current requirements.
Reference: OpenAI: Codex CLI installation. Documentation checked 5 September 2026.
codex login
codex login statusComplete the browser sign-in for the intended account. API-key authentication is a separate option with API billing: pass an already-provisioned OPENAI_API_KEY through stdin using the documented login flow. Never paste a real key into a prompt, shell history or this website. Authentication is distinct from installed-command detection.
Reference: OpenAI: authentication and credential storage. Documentation checked 5 September 2026.
Personal configuration lives in ~/.codex/config.toml. Trusted projects can add .codex/config.toml overrides. Organization policy may constrain settings. Inspect existing configuration before editing; use documented options rather than copying guessed model names or obsolete flags.
Reference: OpenAI: configuration layers. Documentation checked 5 September 2026.
cd "$HOME/Projects/my-project"
codexBegin with a repository inspection request from Chapter 2. Review the active permissions and authorize only the necessary scope. Configuration files and instruction files serve different purposes; neither replaces verification of the result.
Keep project runtimes isolated
Read manifests and lockfiles before installing runtimes. Match the project’s documented versions and package manager. Installing every language globally creates ambiguity without making the project more reproducible.
- Node/npm: inspect package.json, packageManager, engines and any version-manager file. Use the declared manager and committed lockfile. npm ci recreates node_modules and can execute lifecycle scripts; it is a project-changing operation.
- Python: install a supported Python 3 if absent, then create a per-project virtual environment. Do not modify the macOS system Python or use sudo pip.
- Rust: use the project’s rust-toolchain.toml and the official rustup workflow when Rust is needed. Native compilation may need CLT.
node --version
npm --version
python3 --version
rustc --version
cargo --versionpython3 -m venv .venv
source .venv/bin/activate
python -m pip --version
# Install the dependencies declared by this project after reviewing them
python -m pip install -r requirements.txtExclude .venv from Git and recreate it on another machine or architecture. Native Python wheels and Node add-ons must match the runtime architecture; reusing an Intel dependency directory on arm64 can fail. In Node, process.arch reveals the running Node architecture. Cargo targets and native libraries also need to agree.
node -p process.arch
python3 -c "import platform; print(platform.machine())"
rustc -vVReference: Python: virtual environments. Documentation checked 5 September 2026.
Reference: Rust: installation. Documentation checked 5 September 2026.
Diagnose permissions and Mac security separately
Unix mode bits, ownership, access control lists, macOS privacy controls and Gatekeeper are different layers. “Permission denied” does not tell you which layer failed. Inspect the path and operation before changing permissions.
ls -ldeO@ .
ls -l ./scripts/check.sh
# Inspect attributes on a downloaded app; replace with its actual path
xattr -l "/Applications/Example.app"An executable bit allows direct script execution; it does not approve an untrusted application. Grant it only on a reviewed script. Avoid chmod 777 and recursive ownership changes. chown is appropriate only for a diagnosed incorrect owner on a precisely scoped path; administrator privileges do not make a broad repair safe.
chmod u+x ./scripts/check.shGatekeeper evaluates downloaded software, including signing and notarization. A quarantine attribute records download-related metadata. Check the developer and obtain a current trusted release if an app is blocked. Do not remove quarantine recursively or disable Gatekeeper globally.
Reference: Apple: safely opening apps. Documentation checked 5 September 2026.
Privacy permissions can restrict Terminal or your editor from protected locations. Prefer a project directory such as ~/Projects and grant only the app and access actually required in System Settings → Privacy & Security. Full Disk Access is broad and is not a routine prerequisite for Codex. Managed Macs may require an administrator to resolve policy.
Keychain can store credentials, but an environment variable or .env file is not automatically protected by Keychain. Ignore local secret files, provide a dummy .env.example, and review staged content. If a secret was committed, revoke or rotate it and coordinate history cleanup; adding it to .gitignore does not remove it from history.
# Example .gitignore additions; adapt to the repository
.DS_Store
.env
.env.*
!.env.example
.venv/
node_modules/
target/Paths, case sensitivity and symbolic links
APFS can be case-sensitive or case-insensitive. Many Mac volumes are case-insensitive, so an import that differs from the filename only by case may work locally and fail on a case-sensitive deployment host. Check the volume in Disk Utility and preserve exact casing in source, links and Git.
Reference: Apple: APFS formats. Documentation checked 5 September 2026.
pwd
ls -la
# Inspect link destination without changing it
readlink ./current
# Find project instructions; quote the pattern
find . -name 'AGENTS.md' -not -path './node_modules/*'Hidden files begin with a dot; ls -la includes them. A symbolic link references another path, so moving its target can break it. Inspect before copying or deleting through links, and do not assume a deployment service follows them as your workstation does.
# Creates a link only if ./current does not already exist
ln -s ./releases/v1 ./currentAvoid keeping active dependency trees in cloud-synchronized directories when synchronization or placeholder files disrupt builds. This is an operational recommendation, not a prohibition on using Documents. Keep backups and Git remotes appropriate to the project.
macOS command toolkit
These examples use macOS command forms. Run man followed by the tool name on your Mac for the installed implementation. A Linux tutorial may rely on GNU-only flags such as grep -P, find -printf, stat -c or date -d. Installing GNU tools does not make /usr/bin tools change behavior.
Locate and inspect
pwd shows the current directory; ls -la includes hidden files. Quote find patterns so zsh does not expand them first. These commands do not intentionally modify files.
pwd
ls -la
find . -name '*.md' -not -path './node_modules/*'Read and search
Use less for paging (q exits). tail -f follows a growing log until Control-C. Logs may contain secrets; inspect before sharing.
cat README.md
less README.md
head -n 20 README.md
tail -n 50 app.log
grep -n 'TODO' README.mdTransform text without overwriting
These print output. BSD sed differs from GNU sed: on macOS, in-place editing takes a backup-suffix argument, including an explicit empty suffix for no backup. Prefer writing and reviewing a separate output file; do not assume GNU flags work.
sed -n '1,20p' README.md
awk '{ print NR, $0 }' README.mdInspect executable resolution
command -v follows shell command resolution and can identify aliases/functions. which and whereis serve different lookup purposes; neither proves that a binary is the right version or architecture.
command -v git
which git
whereis git
file /bin/zshCheck storage and metadata
macOS stat uses -f for this format, unlike GNU stat -c. du summarizes usage and df checks available filesystem space. The result can differ from Finder due to filesystem accounting.
stat -f '%Sp %Su %z %N' README.md
du -sh .
df -h .Inspect processes and ports
Identify the PID and owner of a listening server before stopping it. lsof may not reveal processes you lack permission to inspect. Avoid printing full process arguments into shared logs.
ps -ax -o pid,ppid,comm
lsof -nP -iTCP:3000 -sTCP:LISTENInspect network responses
This sends a network request for response headers. It does not establish all browser behavior. Do not use curl -k to hide a certificate error; diagnose the trust chain or hostname.
curl -I https://example.comInspect platform and tools
Run brew only if installed. These report a process architecture, OS version, active developer path, Git version and selected Homebrew prefix; they do not certify project compatibility.
uname -m
sw_vers
xcode-select -p
git --version
brew --prefixCreate, copy and move deliberately
mkdir creates directories. cp copies files and mv moves or renames them; a destination collision can overwrite data. Use explicit paths and interactive flags when appropriate.
mkdir -p "$HOME/Projects"
cp -i README.md README.backup.md
mv -i draft.md reviewed.mdDelete files or stop processes only after inspection
rm bypasses Finder’s Trash. kill sends a signal to a process; SIGTERM requests orderly shutdown, while SIGKILL prevents cleanup. Substitute only a verified path or PID. These are examples, not a cleanup sequence.
# Deletes this one file after confirmation
rm -i ./obsolete-draft.txt
# Sends TERM to one verified process; replace 12345 with the correct PID
kill -TERM 12345SSH and file transfer
ssh starts an authenticated remote session; commands there affect that host. scp copies data over SSH and can overwrite its destination. Confirm host identity and destination before sending project files or credentials.
# Example only: substitute your authorized host and destination
ssh [email protected]
scp -i "$HOME/.ssh/id_ed25519_codac" ./release.zip [email protected]:uploads/Resolve the layer that actually failed
“command not found” after installation
Compare command -v output in Terminal and your editor. Read installer PATH instructions, inspect .zprofile and .zshrc, then restart the parent application. Do not reinstall into a second prefix until you know whether the first installation exists.
“bad CPU type” or incompatible architecture
Inspect the binary and runtime architecture. Rebuild native dependencies for the chosen target in an isolated environment. Use a vendor-supported native release; Rosetta is a scoped fallback for Intel software, not a fix for arbitrary mixed libraries.
“invalid active developer path”
Check xcode-select -p and the installed CLT/Xcode version. Use Apple updates or the official installer for the current OS. Confirm the developer directory exists before changing the selected installation.
“operation not permitted”
Separate Unix ownership and mode bits from macOS privacy policy, ACLs and protected locations. Inspect ls -ldeO@ and the exact operation. Grant only a justified app permission; avoid broad Full Disk Access or recursive chown.
A port is already in use
Use lsof with the exact port. Identify the program, stop its dev server normally or choose another project port. Do not send SIGKILL as the first response.
Works locally, fails on deployment
Check filename case, executable bits, line endings, runtime versions and BSD/GNU command assumptions. Reproduce in the target environment. A case-insensitive local volume can conceal broken imports and asset paths.
Package and release for the intended Mac
Web assets, command-line binaries and native .app bundles have different release paths. This guide itself is static HTML; it needs no signing or Mac runtime on the hosting server. A distributed Mac app may need Developer ID signing, notarization and entitlement review.
- Declare minimum macOS and supported architectures from the actual build settings.
- Inspect all native dependencies, plug-ins and embedded helpers; a Universal wrapper does not make an Intel-only library universal.
- Test an installed release artifact on the target architectures and OS versions, outside the development checkout.
- Verify signing, notarization and quarantine behavior using current Apple guidance. Protect certificates and signing credentials.
- Record installation, update, uninstall and recovery behavior.
Do not publish a made-up universal signing command: the correct flags and entitlements depend on the application. Ask Codex to inspect the existing packaging pipeline and produce a bounded release plan before changing it.
Reference: Apple: notarizing Mac software. Documentation checked 5 September 2026.