Development Tools
Master the terminal, Git version control, and GitHub collaboration—the infrastructure every developer and AI agent depends on.
This phase assumes you can: write functions with parameters and return values (Ch 05), use lists and dictionaries (Ch 06), and handle errors with try/except (Ch 09). You'll also need a working Python environment from Ch 03.
Chapter 10 The Terminal and Shell Scripting
Claude Code runs entirely in the terminal. Every command it executes, every file it reads, every test it runs—all happen through the same shell commands you'll learn here. Terminal fluency isn't optional for AI-assisted development; it's the interface.
You used the terminal in Chapter 03 to run Python. Now you'll learn it as a power tool. The ~15 commands below are the ones professionals use daily. Everything else you can look up.
Essential Commands
| Command | What It Does | Example |
|---|---|---|
cd | Change directory | cd ~/projects |
ls | List files | ls -la |
pwd | Print current directory | pwd |
mkdir | Create directory | mkdir src |
rm | Remove file | rm old.txt |
cp | Copy | cp a.py b.py |
mv | Move/rename | mv old.py new.py |
cat | Print file contents | cat README.md |
grep | Search text in files | grep "def " *.py |
head | Show first N lines | head -20 log.txt |
tail | Show last N lines | tail -f server.log |
find | Find files | find . -name "*.py" |
Shell Scripts
A shell script is a file of commands that run in sequence. Start every script with a shebang line that tells the OS which interpreter to use:
#!/bin/bash
echo "Running tests..."
python3 -m pytest
echo "Done."
Save as run_tests.sh, make executable with chmod +x run_tests.sh, then run with ./run_tests.sh.
Piping: Composing Small Tools
The | operator sends the output of one command to the input of another. > writes output to a file.
Environment Variables and PATH
Environment variables are system-wide settings. PATH tells the shell where to find commands. When you get command not found, it means the program isn't in any directory listed in PATH.
export API_KEY="sk-abc123" # set a variable
echo $PATH # see your PATH
SSH Basics
SSH (Secure Shell) lets you connect to remote machines via the terminal. You'll encounter it when deploying code or accessing cloud servers. Generate a key pair with ssh-keygen -t ed25519, then add the public key (~/.ssh/id_ed25519.pub) to remote services (you'll set this up with a specific service in Chapter 12). Connect: ssh user@hostname.
tmux: Terminal Multiplexer
Concept: A terminal multiplexer lets you run multiple persistent shell sessions inside a single terminal window. You can split panes, switch between tabs, and detach entirely—leaving everything running in the background. This is essential for any workflow where you need to monitor multiple processes simultaneously, and it becomes critical when orchestrating multiple AI agents.
tmux (all platforms, free). Install: brew install tmux (macOS) or sudo apt install tmux (Linux). The standard terminal multiplexer for multi-agent workflows in Phase 5.
In Phase 5, you'll run multiple Claude Code agents simultaneously. Each needs its own terminal session. Without tmux, you'd need multiple terminal windows, and closing your laptop kills them all. With tmux, sessions persist in the background—you can disconnect, reconnect, and your agents keep running. tmux is the infrastructure that makes background agents possible.
Installing tmux
Core Concepts
tmux has three layers:
| Concept | What It Is | Analogy |
|---|---|---|
| Session | A named workspace that persists in the background | A project desk that stays set up even when you leave the room |
| Window | A full-screen tab within a session | Different pages on the same desk |
| Pane | A split within a window | Dividing one page into side-by-side columns |
Getting Started
Every tmux command inside a session starts with the prefix key: Ctrl + B. You press the prefix, release it, then press the next key. This is the most common stumbling block—you're pressing a two-step combo, not holding everything at once.
tmux Hotkeys Reference
All hotkeys below assume you press Ctrl + B first (the prefix), release it, then press the next key.
Session Management
| Prefix + | Action | When to Use |
|---|---|---|
d | Detach from session | Leave the session running in the background. Your agents keep working. |
s | List and switch sessions | Interactive session picker. Arrow keys to navigate, Enter to select. |
$ | Rename current session | Give meaningful names: taskforge, api-server, agent-team. |
( / ) | Previous / next session | Cycle between sessions quickly. |
Window Management (Tabs)
| Prefix + | Action | When to Use |
|---|---|---|
c | Create new window | Open a fresh full-screen terminal within the session. |
n | Next window | Cycle forward through windows. |
p | Previous window | Cycle backward through windows. |
0-9 | Jump to window by number | Direct jump: prefix + 2 goes to window 2. |
, | Rename current window | Name windows by purpose: code, tests, logs. |
w | Window list (interactive) | Visual picker showing all windows across all sessions. |
& | Close current window | Confirm with y. Closes all panes in the window. |
Pane Management (Splits)
| Prefix + | Action | When to Use |
|---|---|---|
% | Split vertically (left/right) | Code on the left, test output on the right. |
" | Split horizontally (top/bottom) | Command on top, log stream on the bottom. |
Arrow keys | Move to adjacent pane | Navigate between splits using direction. |
x | Close current pane | Confirm with y. Closes just this pane. |
z | Toggle pane zoom (fullscreen) | Temporarily maximize one pane to see more detail. Press again to restore. |
o | Cycle through panes | Quick hop to the next pane without arrow keys. |
{ / } | Swap pane left / right | Rearrange pane positions without recreating them. |
Space | Cycle through layouts | Toggle between even-horizontal, even-vertical, main-horizontal, etc. |
Ctrl + Arrow | Resize pane by 1 cell | Fine-tune pane sizes. Hold for continuous resize. |
Copy Mode and Scrollback
| Prefix + | Action | When to Use |
|---|---|---|
[ | Enter copy mode | Scroll through output history. Use arrow keys or Page Up/Down. Press q to exit. |
] | Paste from tmux buffer | Paste text you copied in copy mode. |
In copy mode, navigate with arrow keys. Press Space to start selection, move to select text, press Enter to copy. Press q to exit copy mode.
Recommended tmux Configuration
Create ~/.tmux.conf with these settings to make tmux more usable:
# ~/.tmux.conf
# Use Ctrl+A as prefix instead of Ctrl+B (easier to reach)
unbind C-b
set -g prefix C-a
bind C-a send-prefix
# Split panes with | and - (easier to remember)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# New windows open in current directory
bind c new-window -c "#{pane_current_path}"
# Navigate panes with Alt+Arrow (no prefix needed)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# Enable mouse support (click to select pane, scroll to scroll)
set -g mouse on
# Start window numbering at 1 (not 0)
set -g base-index 1
setw -g pane-base-index 1
# Renumber windows when one is closed
set -g renumber-windows on
# Increase scrollback buffer
set -g history-limit 50000
# Reduce escape delay (faster for vim/nano users)
set -sg escape-time 10
# Status bar styling
set -g status-style bg=black,fg=white
set -g status-left "[#S] "
set -g status-right "%H:%M"
After saving, reload with tmux source-file ~/.tmux.conf (or start a new session).
The config changes the prefix from Ctrl + B to Ctrl + A and splits with | and - instead of % and ". All the hotkey tables above use the default bindings. If you use this config, substitute Ctrl + A for Ctrl + B everywhere, and use |/- for splits.
Real-World tmux Workflow
Here's how a typical multi-agent development session looks:
Goal: Create a session with 3 panes. Steps: (1) tmux new -s practice (2) Prefix + % to split vertically (3) Prefix + " to split the right pane horizontally (4) Prefix + arrow keys to navigate between all 3 panes (5) Prefix + z to zoom one pane, then z again to unzoom (6) Prefix + d to detach (7) tmux attach -t practice to reattach. Verification: All 3 panes are still there with their content after reattaching.
Terminal Speed: Advanced Hotkeys
You learned basic shell hotkeys in Chapter 03. Here are the advanced ones that make a real difference when you're working all day in the terminal:
| Hotkey | Action | Example |
|---|---|---|
Ctrl + R | Reverse search history | Start typing any part of a past command. Press Ctrl + R again to cycle through matches. Press Enter to execute, Ctrl + G to cancel. |
Ctrl + X, Ctrl + E | Open current command in $EDITOR | For long, complex commands: edit in a full editor, save, and it executes. Set export EDITOR=nano if not set. |
Alt + . | Insert last argument of previous command | Just ran mkdir -p src/components/auth? Type cd then Alt + . to get cd src/components/auth. |
Ctrl + T | Swap two characters before cursor | Fix typos like sl → ls without retyping. |
Alt + T | Swap two words before cursor | Fix word-order mistakes. |
Ctrl + _ | Undo last edit | Undo a Ctrl + W or Ctrl + U that deleted too much. |
fc | Open last command in editor | Fix and re-run a complex command. Saves/closes to execute. |
History Power Moves
| Command | Action | Example |
|---|---|---|
!! | Repeat entire last command | sudo !! — re-runs previous command with sudo. |
!$ | Last argument of previous command | cat file.py then vim !$ opens the same file in vim. |
!^ | First argument of previous command | Less common, but available. |
!:2 | Second argument of previous command | Access specific positional arguments. |
^old^new | Replace and re-run | Ran git diff mian? Type ^mian^main to fix the typo and re-execute. |
history | grep pytest | Search history for a pattern | Find all past pytest commands. |
TaskForge Connection
You'll use pipes and grep to inspect TaskForge: grep "def " taskforge.py lists all function definitions. When you manage agents in Phase 5, you'll use tmux to run multiple Claude Code sessions simultaneously—one session per agent, each in its own tmux window, all within a single session you can detach from and reattach to.
Micro-Exercises
echo 'hello world' | wc -w
The pipe sends echo's output to the word-counter. Result: 2.
ls | head -5
In one line, find all Python files that contain the word import and count them:
Verification: A number prints (the count of matching files).
If this doesn't work: (1) grep: No such file or directory → you're in an empty directory. cd to a project first. (2) Count is 0 → no Python files in the current tree.
Interactive Exercises
Knowledge Check
Which command shows only the first 10 lines of a file?
Knowledge Check
What does the pipe operator | do in cat file.txt | grep error?
Knowledge Check
Which flag makes grep search case-insensitively?
Terminal Proficiency
File Count
Run ls -la in your TaskForge directory and paste the output below.
Chapter 11 Version Control with Git
Git is not optional. Claude Code creates branches, makes commits, and reads your project history to understand your codebase. Without Git, you can't use Claude Code effectively—and without understanding Git, you can't supervise what Claude Code does with your code.
You spend 3 hours refactoring a file. You break something. You can't remember what the working version looked like. You try to undo manually and break it worse. With Git, one command (git checkout -- file.py) restores the last working version. Without it, you're gambling with every edit.
Git is the universal version control system. Virtually all professional projects use it. Install: brew install git (macOS), apt install git (Linux), or git-scm.com (Windows).
Git tracks every change to your code. Think of it as a time machine for your project—you can go back to any previous state. Branches are like parallel timelines: you can work on a new feature in one timeline while keeping the main timeline stable. If the experiment fails, you discard that timeline. If it succeeds, you merge it back. This is how AI agents work on your code without breaking what already works.
Claude Code reads your git history, makes commits, creates branches, and resolves conflicts. If you don't understand what it's doing with git, you can't supervise it.
Core Commands
| Command | What It Does |
|---|---|
git init | Create a new repo |
git add . | Stage all changes |
git commit -m "msg" | Save a snapshot |
git status | See what's changed |
git log --oneline | View commit history |
git diff | See unstaged changes |
git branch name | Create a branch |
git switch name | Switch to a branch |
git merge name | Merge a branch into current |
Merge Conflicts
When two branches change the same line, git can't decide which version to keep. It marks the conflict with <<<<<<</=======/>>>>>>> markers. You choose the correct version, delete the markers, and commit.
Worktrees
git worktree add ../feature-branch feature creates a separate working directory for a branch. Claude Code multi-agent setups use worktrees so each agent has its own filesystem.
TaskForge Connection
In the Phase 3 Gate, you'll turn TaskForge into a git repo with 3+ commits and a merged branch. This is how every professional project is managed.
Micro-Exercises
git init
echo "# My Project" > README.md
git add README.md
git commit -m "initial commit"
git log --oneline
git branch feature
git switch feature
echo "new feature" > feature.txt
git add feature.txt
git commit -m "add feature"
git switch main
The file feature.txt disappears—it's on the other branch.
Init a repo, commit a file, create branch test, modify line 1 on test, switch to main, modify the same line 1 differently, git merge test. You'll get a conflict. Open the file, resolve it, commit.
Verification: git log --oneline shows at least 4 commits. The conflict markers are gone.
If this doesn't work: (1) fatal: not a git repository → you forgot git init. (2) No conflict → you modified different lines, not the same line. (3) CONFLICT message but file looks normal → open the file in an editor to see the markers.
Interactive Exercises
Knowledge Check
What is the Git staging area (index)?
Knowledge Check
What does git diff --staged show?
Git Fundamentals
Git Log
Run git log --oneline in your TaskForge repo and paste the output.
Complete the "Main" section of Learn Git Branching. It's an interactive visual tutorial that makes branching and merging click in a way that reading alone cannot.
Chapter 12 Git Remote and GitHub
Every professional project lives on a remote repository. Claude Code workflows in later phases involve pull requests, remote branches, and CI pipelines—all of which require understanding remote Git.
What Is a Remote?
So far, all your Git work has been local—commits, branches, and merges living only on your machine. A remote is a copy of your repository hosted on another server, typically GitHub. Your local repo is where you work. The remote repo is where you share, back up, and collaborate. Think of it this way: your local repo is your notebook; the remote is the shared whiteboard everyone on the team can see.
When you push, you copy your commits to the remote. When you pull, you copy the remote's commits to your local repo. The two stay in sync through these explicit actions—nothing happens automatically.
GitHub Account Setup
If you don't have a GitHub account yet:
- Go to github.com and click Sign up.
- Choose a username, enter your email, and set a password.
- Once logged in, click the + icon (top right) → New repository.
- Name it
taskforge, leave it public, and do not initialize with a README (you already have one locally). - GitHub shows you the commands to connect your local repo. You'll use those next.
Connecting Local to Remote
After creating an empty repository on GitHub, connect your local repo to it:
git remote add origin https://github.com/YOUR-USERNAME/taskforge.git
origin is the conventional name for your primary remote. You can name it anything, but origin is universal. Verify the connection:
git remote -v
You should see two lines—one for fetch and one for push—both pointing to your GitHub URL.
Push and Pull
Push sends your local commits to the remote. Pull fetches the remote's commits and merges them into your local branch.
# Send your commits to GitHub
git push origin main
# Get new commits from GitHub
git pull origin main
The first time you push a brand-new branch, use -u to set up tracking:
git push -u origin main
After that, git push and git pull work without specifying the remote and branch every time.
If you see ! [rejected] main -> main (fetch first), it means the remote has commits you don't have locally. Fix it:
git pull origin main # fetch and merge remote changes
git push origin main # now push succeeds
If the pull creates a merge conflict, resolve it the same way you learned in Chapter 11: edit the file, remove the conflict markers, git add, and git commit.
Cloning
git clone downloads an entire repository—all files, all history, all branches—from a remote to your machine:
git clone https://github.com/someone/their-project.git
This creates a new directory named their-project/ with a fully functional Git repo inside. The remote origin is already set up, pointing back to the URL you cloned from. This is how you get someone else's project onto your machine.
Branches on Remotes
Branches exist independently on your local machine and on the remote. To push a local branch to GitHub:
git push origin feature-branch
This creates the branch on GitHub if it doesn't exist. To set up tracking (so Git knows the local branch corresponds to the remote one):
git push -u origin feature-branch
After tracking is set, git push and git pull automatically know which remote branch to use. To see all branches, including remote ones:
git branch -a
.gitignore
A .gitignore file tells Git which files and directories to never track. This keeps your repository clean by excluding generated files, secrets, and environment-specific artifacts.
# Byte-compiled Python files
*.pyc
__pycache__/
# Virtual environments
.venv/
venv/
# Environment variables / secrets
.env
# IDE settings
.vscode/
.idea/
# OS files
.DS_Store
# Node (if applicable)
node_modules/
Pattern syntax:
| Pattern | What It Matches |
|---|---|
*.pyc | All files ending in .pyc, in any directory |
__pycache__/ | Any directory named __pycache__ and everything inside it |
.env | A file named .env in the project root |
.venv/ | The .venv directory and all its contents |
node_modules/ | The node_modules directory (JavaScript dependencies) |
Without .gitignore, you risk committing secrets (.env files with API keys), bloating your repo with hundreds of megabytes of dependencies (node_modules/, .venv/), and pushing OS junk (.DS_Store) that clutters every diff. Add .gitignore before your first commit.
Pull Requests
A pull request (PR) is a proposal to merge one branch into another. It's the core of code review: you show your changes, teammates review them, and only after approval does the code merge into main. The workflow:
- Branch: Create a feature branch locally.
git switch -c add-delete-feature - Work: Make changes, commit them.
- Push:
git push -u origin add-delete-feature - Open PR: Go to GitHub. You'll see a banner: "Compare & pull request." Click it. Write a description of what you changed and why.
- Review: Teammates read your code, leave comments, request changes.
- Merge: Once approved, click "Merge pull request" on GitHub.
Even solo developers use PRs—they create a clean record of what changed and why, and they're how Claude Code submits work for your review in later phases.
SSH Keys
GitHub supports two connection methods: HTTPS and SSH. HTTPS asks for credentials on every push. SSH uses a key pair so you authenticate automatically.
Generate an SSH key:
ssh-keygen -t ed25519 -C "your-email@example.com"
Press Enter to accept the default location. Set a passphrase or leave it empty. Then add the public key to GitHub:
- Copy your public key:
cat ~/.ssh/id_ed25519.pub - GitHub → Settings → SSH and GPG keys → New SSH key
- Paste the key and save.
Test the connection:
ssh -T git@github.com
You should see: Hi username! You've successfully authenticated. Now you can use SSH URLs (git@github.com:user/repo.git) instead of HTTPS.
Common Misconceptions
No. git push only sends commits the remote doesn't already have. If you've made 100 commits and the remote has 98, push sends only the 2 new ones. Git is efficient—it transfers the minimum necessary data.
No. Deleting a file on your machine changes nothing on the remote. The deletion only reaches GitHub after you git add the deletion, git commit, and git push. Until then, the file still exists on GitHub exactly as it was.
TaskForge Connection
Push TaskForge to a GitHub repository. Create a .gitignore that excludes __pycache__/, .venv/, *.pyc, and .env. Create a feature branch, push it to GitHub, and open a pull request—even if you're the only reviewer. This is the same workflow you'll use when Claude Code creates branches and PRs for your review in Phase 4.
Micro-Exercises
Create a new GitHub repository and push TaskForge to it:
git remote add origin https://github.com/YOUR-USERNAME/taskforge.git
git push -u origin main
Verify by visiting your GitHub repo URL—your files and commits should appear.
Create a .gitignore file in your TaskForge project root:
__pycache__/
.venv/
*.pyc
Stage, commit, and push:
git add .gitignore
git commit -m "add .gitignore"
git push
Create a branch, push it, and open a pull request on GitHub:
git switch -c add-delete-feature
# make a change (e.g., add a delete command to TaskForge)
git add .
git commit -m "add delete feature"
git push -u origin add-delete-feature
Then go to GitHub. Click "Compare & pull request." Write a description and submit. You now have a PR—even if you merge it yourself.
Fork a public Python repository on GitHub. Clone your fork. Create a branch, make a small improvement (fix a typo, improve a docstring), push your branch, and open a pull request.
Verification: You have a PR visible on GitHub with your changes.
If this doesn't work: (1) If push is rejected, try git pull --rebase origin main first. (2) If SSH fails, check ssh -T git@github.com to verify your key is set up. (3) If you can't find the fork button, make sure you're logged into GitHub and viewing someone else's public repository.
In Phase 5, you'll review git diff output from AI agents every day. The format below is the same one Git uses when you run git diff. Lines starting with + were added. Lines starting with - were removed. The @@ line shows where in the file the change happened. Practice reading this now—it's the same skill, just a different context.
Review an AI Agent's Diff
An AI agent was asked: "Add error handling to the delete_task function." Below is the diff it produced. Read it carefully, then write a function evaluate_diff() that returns a dict with three keys: "correct_changes" (list of line numbers with valid error-handling additions), "unrelated_change" (the line number of a change that wasn't requested), and "missing" (a string describing what the agent forgot to handle).
Lines with + are additions. The isinstance check is relevant error handling. But what about the list_tasks change?
The agent was only asked to add error handling to delete_task. Changing list_tasks is scope creep—an unrelated change.
What happens if tasks is None or not a list? The agent added a type check for task_id but forgot to validate tasks itself.
Interactive Exercises
Knowledge Check
What does the -u flag do in git push -u origin main?
Knowledge Check
What is the primary purpose of a pull request?
GitHub Workflow
Remote URL
Run git remote -v in your TaskForge repo and paste the output.
Phase 3 Gate Checkpoint & TaskForge on GitHub
Minimum Competency
Use terminal commands fluently, create git branches and resolve conflicts, push to a remote repository, and collaborate via pull requests.
Your Artifact
TaskForge pushed to GitHub with a .gitignore, at least 3 commits, and a feature branch merged via pull request.
Verification
Your TaskForge repo is visible on GitHub. git log --oneline shows 3+ commits. The merged PR is visible in the Pull Requests tab.
If you can't push to GitHub or create a PR → return to Chapter 12.
What You Can Now Do
- Navigate and compose terminal commands fluently
- Use Git for version control: commits, branches, merges, conflict resolution
- Push code to GitHub and pull remote changes
- Collaborate through pull requests and code review
- Keep repositories clean with
.gitignore
You now have the development tools that professional workflows depend on. Phase 4 builds on this foundation with APIs and HTTP, building APIs with Flask, database basics, Docker containers, CI/CD with GitHub Actions, and project architecture. You'll go from pushing code to GitHub to deploying full applications.