Part 1: Why CLI-First
Most AI coding tool demos show a GUI: a sidebar chat panel, inline suggestions appearing in an editor, a button you click to accept changes. That workflow is fine for developers who work in VS Code or Cursor all day. But if your daily environment looks more like this —
$ tmux new-session -s work
$ vim src/api/routes.ts
$ npm test -- --watch
$ git log --oneline -10
$ ssh prod-server 'tail -f /var/log/app.log'
— then GUI-based AI tools are a context switch, not a productivity boost. You have to leave your flow, open a browser or a different editor, copy-paste code back and forth, and then return to where you were.
CLI-first AI development keeps you in the terminal. The advantages are significant:
- No context switching — AI runs in the same environment as your code, your tests, and your git history. Everything is one
Ctrl-Bpane switch away. - Composability — CLI tools can be piped, scripted, and combined with other Unix tools.
find | xargs | aiis a workflow that GUIs can't replicate. - Scriptability — Anything you do once in the terminal, you can automate. AI review on every commit. Documentation generation in CI. Test scaffolding as a shell alias.
- SSH-friendly — Works on remote servers, in containers, over slow connections. No GUI rendering required.
- Reproducible — Terminal sessions can be logged, shared, and replayed. A shell script is documentation of your process.
The best AI tool is the one that integrates into your existing workflow, not the one that replaces it. If your workflow is terminal-based, your AI tools should be terminal-based.
Part 2: Claude Code in Depth
Claude Code is Anthropic's CLI tool for AI-assisted development. It runs in your terminal, reads your project files, executes commands, and makes edits — all through a conversational interface that feels like pair programming with someone who can actually touch the codebase.
Installation and Setup
# Install globally via npm
$ npm install -g @anthropic-ai/claude-code
# Verify installation
$ claude --version
# Start an interactive session in your project directory
$ cd your-project
$ claude
Claude Code needs either a Claude Pro/Max subscription or an Anthropic API key. On first launch it walks you through authentication.
The CLAUDE.md File
This is the most important file in your project for CLI-first development. Create a CLAUDE.md in your project root, and Claude Code reads it automatically at the start of every session. It's your project's AI instruction manual.
# Project: TaskFlow API
## Stack
- TypeScript, Express, SQLite (better-sqlite3)
- Vitest for testing
- Zod for validation
## Conventions
- All async functions use Result<T, AppError> return type
- Database access through repository classes only, never direct queries
- Error codes: DOMAIN_ACTION_REASON (e.g. USER_CREATE_EMAIL_TAKEN)
- API responses: { data: T } on success, { error: string, code: string } on failure
## Commands
- `npm test` — run all tests
- `npm run test:watch` — run tests in watch mode
- `npm run lint` — run ESLint
- `npm run build` — TypeScript compilation
## Architecture
- src/routes/ — Express route handlers
- src/repos/ — Repository classes (one per table)
- src/middleware/ — Auth, validation, error handling
- src/types/ — Shared TypeScript types
- tests/ — Mirrors src/ structure
## Rules
- Never use `any` type
- Always validate inputs with Zod at route level
- All database queries must be parameterized
- Tests must cover happy path + at least one error case
Every session starts with Claude Code reading this file. You never re-explain your stack, your conventions, or your project structure. Write it once, benefit every session.
Pro Tip: Evolve CLAUDE.md Over Time
When Claude Code makes a mistake that your CLAUDE.md should have prevented — wrong naming convention, wrong error pattern, wrong test structure — add a rule to the file immediately. After a few weeks, your CLAUDE.md becomes a comprehensive project guide that prevents nearly all common AI errors. It's also valuable documentation for human team members.
The Iterative Loop
Claude Code's defining feature is the ability to take action, verify the result, and adjust. This is the loop that makes it reliable for real work:
Add rate limiting to all auth endpoints. Use express-rate-limit. Max 5 attempts per IP per 15 minutes on login and register. Add tests.
I'll add rate limiting to the auth routes. Let me:
1. Install express-rate-limit
2. Create the rate limiter middleware
3. Apply it to login and register routes
4. Write tests
5. Run the test suite to verify
Claude Code then executes each step: runs npm install, edits the route files, creates test files, and runs npm test. If tests fail, it reads the output, fixes the issue, and runs again. This loop — edit → run → read output → fix → run — continues until tests pass.
This is fundamentally different from a chat interface where you copy-paste code back and forth. Claude Code closes the feedback loop because it can verify its own work.
Practical Session Patterns
# Start a session and make a focused change
$ claude "Add input validation to all POST endpoints using Zod"
# Ask it to explore before acting
$ claude "Review src/routes/tasks.ts and list potential bugs. Don't fix yet."
# Multi-step work
$ claude "Refactor the user repository to use prepared statements,
update all routes that use it, and run tests after each change"
# Quick one-shot
$ claude "What does the function on line 47 of src/utils/auth.ts do?"
# Use with a specific file
$ claude "Add JSDoc to all exported functions in src/repos/userRepo.ts"
Slash Commands
Inside an interactive Claude Code session, several slash commands control behavior:
/clear— Reset conversation context (useful when switching tasks)/compact— Summarize conversation to free up context window/cost— Show token usage and cost for current session/help— Show all available commands
In long sessions, Claude Code's context window fills up. When that happens, responses degrade — the same context drift problem from the manual's field guide, but in the terminal. Use /compact proactively when switching tasks, and start new sessions for unrelated work.
Part 3: Aider for Open-Source Workflows
Aider is an open-source terminal AI coding tool. Where Claude Code is Anthropic's integrated product, Aider is a bring-your-own-model tool with strong git integration and a transparent codebase you can inspect and modify.
Setup
# Install via pip
$ pip install aider-chat
# Configure with Claude (recommended)
$ export ANTHROPIC_API_KEY=sk-ant-...
# Or with OpenAI
$ export OPENAI_API_KEY=sk-...
# Start in your project directory
$ cd your-project
$ aider
The Git-First Approach
Aider's distinctive feature is its tight git integration. Every change Aider makes becomes a git commit automatically. This means:
- You can review every AI-generated change with
git difforgit log - Reverting a bad change is just
git revert HEAD - Your git history shows exactly what AI changed and what you changed manually
- Branching and merging work normally — AI changes are just commits
$ aider src/routes/auth.ts src/middleware/rateLimit.ts
aider> Add rate limiting middleware and apply it to the login route.
# Aider edits both files and auto-commits:
# "feat: Add rate limiting to login route"
$ git log --oneline -3
a4f2c1d feat: Add rate limiting to login route
b8e3d2a refactor: Extract auth helpers
c1a4f5b fix: Handle null user in profile endpoint
Adding Files to Context
You specify which files Aider can read and edit by adding them at launch or during the session. This explicit file selection is both a feature and a safety mechanism — Aider only touches files you've approved.
# Start with specific files
$ aider src/routes/auth.ts src/types/user.ts
# Add more files during session
aider> /add src/middleware/auth.ts
# Add read-only files for context (won't be edited)
aider> /read-only src/types/shared.ts
# Drop files from the session
aider> /drop src/types/user.ts
When to Use Aider vs Claude Code
- Aider — When you want fine-grained git control, model flexibility (switch between Claude, GPT-4, local models), open-source transparency, or you're on a team where everyone needs to understand what the AI tool does.
- Claude Code — When you want the AI to run commands, execute tests, and iterate autonomously. Claude Code's ability to close the feedback loop (edit → test → fix) makes it stronger for complex multi-step tasks.
- Both — Some developers use Aider for focused edits (change this function, add this test) and Claude Code for larger tasks (refactor this module, add validation across all endpoints).
Part 4: Shell Patterns
Beyond dedicated tools, you can integrate AI directly into your shell workflow using the Anthropic API, simple scripts, and Unix pipes. These patterns are lightweight, composable, and work anywhere you have curl.
The Basic Shell Helper
# Quick AI query from anywhere in the terminal
ask() {
curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "content-type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d "{
\"model\": \"claude-sonnet-4-20250514\",
\"max_tokens\": 1024,
\"messages\": [{\"role\": \"user\", \"content\": \"$*\"}]
}" | jq -r '.content[0].text'
}
# Quick questions without leaving the terminal
$ ask "What's the difference between useMemo and useCallback in React?"
# Explain an error
$ ask "Explain this error: ECONNREFUSED 127.0.0.1:5432"
# Generate a command
$ ask "Give me a find command that deletes all .log files older than 7 days"
Piping Code to AI
The real power of shell-based AI is composition with pipes. Send code directly to AI for analysis, review, or transformation:
# Review a specific file
$ cat src/auth.ts | ask "Review this code for security issues"
# Explain a complex function
$ sed -n '45,90p' src/utils/parser.ts | ask "Explain what this function does"
# Generate tests for a module
$ cat src/repos/userRepo.ts | ask "Write Vitest tests for all public methods"
# Find and review recent changes
$ git diff HEAD~3 | ask "Summarize these changes and flag any concerns"
# Analyze error logs
$ tail -50 /var/log/app.log | ask "What errors are recurring? Suggest fixes."
The Code Review Alias
# AI review of staged changes before commit
ai-review() {
local diff=$(git diff --cached)
if [ -z "$diff" ]; then
echo "No staged changes to review."
return 1
fi
echo "$diff" | ask "Review this git diff. Check for:
1. Bugs or logic errors
2. Security issues
3. Missing error handling
4. Naming inconsistencies
Be concise. Only mention real issues."
}
$ git add -p # Stage changes interactively
$ ai-review # AI reviews before you commit
$ git commit -m "feat: ..." # Commit with confidence
Batch Processing with find + xargs
# Generate documentation for undocumented files
$ find src/routes -name '*.ts' -exec sh -c '
if ! grep -q "/**" "$1"; then
echo "Documenting: $1"
cat "$1" | ask "Add JSDoc to all exported functions. Return the complete file." > "$1.tmp"
mv "$1.tmp" "$1"
fi
' _ {} \;
Shell scripts that pipe files through AI and write results back are powerful but dangerous. Always review the output before committing. A safer pattern: write to .tmp files first, diff them against originals, then replace only after manual review. Or work on a branch and review the full diff before merging.
Part 5: The Terminal Workflow
Here's how a full CLI-first AI development session actually looks. This is the layout, the flow, and the habits that make it productive.
The tmux Layout
# Create a development session
$ tmux new-session -s dev
# Split into three panes:
# ┌─────────────────┬──────────────────┐
# │ │ │
# │ Editor │ Claude Code │
# │ (vim/nvim) │ or Aider │
# │ │ │
# ├─────────────────┴──────────────────┤
# │ Tests / Shell / Logs │
# └────────────────────────────────────┘
# Pane 0: Editor
$ vim src/routes/tasks.ts
# Pane 1: AI assistant
$ claude # or: aider src/routes/tasks.ts
# Pane 2: Tests in watch mode
$ npm test -- --watch
Three panes, three roles: you edit in one, AI works in another, tests run continuously in the third. When AI makes a change, you see tests pass or fail immediately in the bottom pane. When you make a manual edit, same thing.
A Typical Development Flow
Here's 30 minutes of real CLI-first development on a feature:
tmux layout open. Claude Code running. Tests watching. Open the relevant files in vim.git diff to review everything. ai-review alias for a final check. git add -A && git commit.The entire session happens in one terminal window. No browser tabs, no file uploads, no copy-paste between applications. Your hands stay on the keyboard, your eyes stay on code, and the feedback loop between "ask AI" and "see tests pass" is measured in seconds, not minutes.
Part 6: Automation Scripts
The ultimate expression of CLI-first AI development: scripts that run AI operations without you being present. These are the building blocks for integrating AI into your development pipeline.
Pre-Commit AI Review
#!/bin/bash
# AI review on every commit — catches issues before they land
DIFF=$(git diff --cached --diff-filter=ACMR)
[ -z "$DIFF" ] && exit 0
REVIEW=$(echo "$DIFF" | curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "content-type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d @- << EOF | jq -r '.content[0].text'
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": "Review this diff for bugs, security issues, and obvious mistakes. If everything looks fine, respond with just 'LGTM'. If there are issues, list them briefly.\n\n$(echo "$DIFF" | jq -Rs .)"
}]
}
EOF
)
if echo "$REVIEW" | grep -qi "LGTM"; then
echo "✓ AI review: looks good"
exit 0
else
echo "⚠ AI review found issues:"
echo "$REVIEW"
echo ""
read -p "Commit anyway? (y/N) " -n 1 -r
echo
[[ $REPLY =~ ^[Yy]$ ]] && exit 0 || exit 1
fi
Changelog Generator
#!/bin/bash
# Generate changelog entry from recent commits
SINCE=${1:-"last week"}
COMMITS=$(git log --since="$SINCE" --pretty=format:"%h %s" --no-merges)
if [ -z "$COMMITS" ]; then
echo "No commits since $SINCE"
exit 0
fi
ask "Generate a concise changelog from these git commits.
Group by: Added, Changed, Fixed, Removed.
Skip trivial commits (typos, formatting).
Use past tense. One line per item.
Commits:
$COMMITS"
$ ./scripts/changelog.sh "2 weeks ago"
## Changes (Feb 10 — Feb 24)
### Added
- Rate limiting on authentication endpoints
- Task assignment with user validation
- Batch export endpoint for project data
### Changed
- Migrated user repository to prepared statements
- Updated error codes to DOMAIN_ACTION_REASON format
### Fixed
- Null pointer when accessing archived project tasks
- Race condition in concurrent task position updates
Test Scaffolding
#!/bin/bash
# Generate test file for a source file that doesn't have one
SRC_FILE=$1
if [ -z "$SRC_FILE" ]; then
echo "Usage: gen-tests.sh <source-file>"
exit 1
fi
# Derive test file path
TEST_FILE=$(echo "$SRC_FILE" | sed 's|src/|tests/|' | sed 's|\.ts$|.test.ts|')
if [ -f "$TEST_FILE" ]; then
echo "Test file already exists: $TEST_FILE"
exit 1
fi
mkdir -p $(dirname "$TEST_FILE")
cat "$SRC_FILE" | ask "Write Vitest tests for this TypeScript module.
Cover: happy path, error cases, edge cases.
Use describe/it blocks. Mock external dependencies.
Import from the relative path to the source file.
Return only the test file contents, no explanation." > "$TEST_FILE"
echo "Created: $TEST_FILE"
echo "Run: npx vitest $TEST_FILE"
$ ./scripts/gen-tests.sh src/repos/taskRepo.ts
Created: tests/repos/taskRepo.test.ts
Run: npx vitest tests/repos/taskRepo.test.ts
$ npx vitest tests/repos/taskRepo.test.ts
✓ tests/repos/taskRepo.test.ts (8 tests)
✓ TaskRepo > create > creates a task with valid input
✓ TaskRepo > create > rejects missing title
✓ TaskRepo > findByProject > returns tasks for project
✓ TaskRepo > findByProject > returns empty for unknown project
...
Batch Documentation
#!/bin/bash
# Find and document all undocumented exported functions
echo "Scanning for undocumented exports..."
COUNT=0
for file in $(find src -name '*.ts' -not -path '*/node_modules/*'); do
# Check if file has exports without JSDoc
if grep -q '^export' "$file" && ! grep -q '/\*\*' "$file"; then
echo " Documenting: $file"
DOCUMENTED=$(cat "$file" | ask "Add JSDoc comments to all exported functions and types.
Keep all existing code exactly as-is. Only add documentation.
Return the complete file.")
echo "$DOCUMENTED" > "${file}.documented"
COUNT=$((COUNT + 1))
fi
done
echo ""
echo "Documented $COUNT files. Review with:"
echo " diff src/routes/tasks.ts src/routes/tasks.ts.documented"
echo ""
echo "Apply with:"
echo " for f in \$(find src -name '*.documented'); do mv \"\$f\" \"\${f%.documented}\"; done"
Pro Tip: The .documented Pattern
Notice the script writes to .documented files instead of overwriting originals. This is the safe pattern for batch AI operations: generate to temporary files, review the diffs, then apply. Never let a batch script overwrite source files directly — one hallucinated response can corrupt a file.
Getting Started
You don't need to set up everything at once. Start with what fits your current workflow:
Add the ask function to your shell
Five lines in your .bashrc. Gives you instant AI access from anywhere in the terminal. Use it for a week.
Try Claude Code on a real task
Install it, create a CLAUDE.md, and use it for one feature. The iterative test loop will either click or it won't — but you need to try it on real work to know.
Set up the tmux layout
Editor + AI + tests. Three panes. Use it for a full development session. The flow state difference is immediate.
Add one automation script
The pre-commit review hook or the test generator — whichever solves a real annoyance in your current workflow.
CLI-First AI Development — Summary
- Claude Code — Interactive AI in the terminal with file access, command execution, and iterative test loops. CLAUDE.md is your project's AI instruction manual.
- Aider — Open-source, git-integrated, model-flexible. Every AI edit is an automatic commit you can review and revert.
- Shell patterns — The
askfunction, pipe patterns, and theai-reviewalias bring AI into any terminal workflow without dedicated tools. - tmux layout — Editor + AI + tests in three panes. Immediate feedback loops without leaving the terminal.
- Automation scripts — Pre-commit review, changelog generation, test scaffolding, batch documentation. AI in your pipeline, not just your editor.
- Safety patterns — Write to temporary files, review diffs, work on branches. Never let batch AI scripts overwrite source files directly.