Claude Code: From Zero to Fort
A Build Log from Five Months In

Claude Code: From Zero to Fort

I built this with Claude Code itself. You’ll learn by watching over my shoulder, not from a lecture. Take chances, make mistakes, get messy — it’s gonna be a blast.

Corey Thomas
March 2026
25 min read
zero·fort

I’ve been using AI tools since GPT-3.5 was the hot new thing and Midjourney was still a janky Discord bot. ChatGPT, Claude, Copilot — I’ve run the gauntlet. They all had the same shape though. You paste something in, you get something back, you paste the next thing. Context resets. You’re the glue.

Claude Code broke that pattern. First time I ran it, it was just.. sitting there. In my terminal. Looking at my files. I told it to fix a bug and instead of handing me a code block, it opened the file, read the code around it, fixed the problem, and ran the tests. I didn’t copy anything. I didn’t paste anything. I just watched.

This is that story — five months of building with it, the patterns that stuck, the stuff that broke, and the workspace that emerged. I’m a designer, not a developer. Everything here I built with Claude Code itself. You’ll learn by watching over my shoulder, not from a lecture.

Part 1Getting Started

Not a chatbot — a collaborator

This took me a minute to internalize. Every other AI tool I’d used was a conversation aboutmy work. Claude Code is different — it’s sitting in your project, reading your actual files, and when you tell it to do something it just.. does it. Opens the file. Reads the code around the problem. Makes the fix. Runs the tests. Checks if the tests passed. Fixes what it broke. You describe the problem once and watch.

  • It sees the whole project.Every file, every folder, every config — not just the snippet you remembered to copy.
  • It does the work. Reads files, writes changes, runs commands, checks what happened. You say what. It figures out how.
  • It checks its own homework.Runs your tests, catches its own mistakes, fixes them — you just approve.
  • It builds context as it goes. Ask it to fix three things and by the third one it already knows how your project is wired.
Before — Web AI

Copy → Paste → Repeat

Paste code into a chat. Copy the answer back. Context resets every message. You do all the work.

After — Claude Code

Describe → Approve → Done

Describe what you want. Claude reads your files, makes changes, runs tests, fixes mistakes. You approve each step.

★ Insight

Biggest shift in how I talk to it: tell it the what and why, not the how. “The error message doesn’t clear when the user starts typing” beats “on line 47 of LoginForm.tsx, change the onChange handler.” Give it the problem. Let it find the file, read the code, figure out the fix. Works the same for non-code — “make this data easier to scan” beats “create a pivot table with columns A through D.”

What counts as a “project”?

A folder on your computer with files in it. That’s it. Doesn’t have to be code.

If you write code

  • A web app, API, mobile app, script collection
  • Open source contributions, side projects, work repos

If you don’t write code (yet)

I’ve pointed Claude Code at a pile of PDFs and said “make sense of this.” Worked. Pointed it at a folder of notes and said “organize these by topic.” Also worked. It operates on files— code just happens to be one kind.

Parsed documents, reorganized entire note collections, generated spreadsheets from raw data. Did I know how to do any of that programmatically? No. I described what I wanted and Claude figured out the approach.

If your thing lives in files, Claude Code can work with it. If it doesn’t exist yet, Claude Code can help you build it.

Installation

One command. Auto-updates. Done.

bash
# Install via npm (Node.js 18+ required)
npm install -g @anthropic-ai/claude-code

On Windows you’ll need WSL — Claude Code is a Unix tool. First launch opens your browser to authenticate. After that, you’re in.

Your first session

Terminal 101 — skip if you live here

Claude Code runs in your terminal— the text-based interface underneath your computer’s GUI. Never opened one? Here’s how:

  • Mac:Spotlight → “Terminal”
  • Windows: Install WSL first, then open Ubuntu
  • Linux: You probably already have three open

cdmeans “change directory” — a directory is just a folder. The ~ symbol is your home folder. No project yet? Make an empty folder. Claude will help you fill it.

Terminal
# Three commands you need
ls # list files
cd ~/my-app # go to a folder
pwd # where am I?

Starting from scratch

Terminal
mkdir ~/my-first-project # create a folder
cd ~/my-first-project # go into it
claude # start Claude Code

╭─────────────────────────────╮
│ Claude Code │
│ Ready. What would you │
│ like to work on? │
╰─────────────────────────────╯

What to say first

Depends what you’re walking in with:

prompts
# If you have a code project:
What is this project? Walk me through the architecture.

# If you have files (notes, data, documents):
What's in this folder? Summarize what you find.

# If you're starting from nothing:
Help me build a personal budget tracker as a simple HTML page.

# If you're just curious:
Create a fun interactive webpage that lets me design pixel art.

That first prompt — “what is this project” — was a revelation for me. Claude reads your package.json, your README, your source code, and gives you a structural picture of what’s there. Then try something with teeth: “The login page has a bug where error messages don’t clear when the user starts typing. Fix it.”

Watch what happens. It finds the files, reads them, makes the edit, runs tests to check its work. You approve each step. The first time is genuinely strange — watching an AI actually do things on your machine. That wears off fast.

Why it keeps asking permission

First few minutes, everything needs your approval. Read a file? Approve. Edit something? Approve. Run a command? Approve. Tedious, but intentional.

Hit “always”on a tool pattern and Claude stops asking for that type during the session. Trust builds incrementally — safe reads get auto-approved fast, destructive stuff stays gated.

How permissions work in practice
Read fileAlways allow
Edit fileAlways allow
npm testAlways allow
git pushAsk each time
rm -rfAsks for confirmation
npm installAsk each time

It won’t run rm -rf or git push without checking with you first. Destructive commands always get a prompt. The system is building trust, not adding friction.

Started with maybe 20 allowed commands. That list grew over months, not an afternoon. Every addition came from getting tired of approving the same thing for the tenth time.

Part 2Leveling Up

Your project’s brain: CLAUDE.md

Single highest-leverage thing I found. Drop a CLAUDE.mdfile at the root of your project and it loads every time a session starts. This is where you put the stuff Claude can’t figure out by reading your code — the unwritten rules, the gotchas, the “don’t touch that” warnings.

What to put in it

markdown
# My Project

## Build & Test
- `pnpm dev` — start dev server
- `pnpm test` — run tests (vitest)
- `pnpm lint` — eslint + prettier check

## Code Style
- TypeScript strict mode, no `any` types
- Use ES modules, not CommonJS
- React components use function declarations, not arrow functions
- State management: Zustand (not Redux, not Context)

## Architecture
- `/src/api/` — all API calls go through typed client in `api/client.ts`
- `/src/hooks/` — custom hooks, one per file, prefixed with `use`
- Database: Prisma ORM, migrations in `/prisma/migrations/`

## Gotchas
- The `auth` middleware must run before `cors` — order matters in `server.ts`
- Don't import from `@/internal/` — those modules are deprecated
- Test DB uses SQLite, prod uses PostgreSQL — check for SQL dialect issues

What NOT to put in it

  • Code snippets that’ll go stale. Point to the file instead: “See src/api/client.tsfor the API client pattern.”
  • Obvious stuff your code already says. Claude can read your package.json — it knows what framework you’re using.
  • Novels. CLAUDE.md is advisory, not law. Claude follows it about 80% of the time. Keep it tight — ask yourself “would removing this line cause a mistake?” for each entry.
  • Rules that must work 100% of the time. Those belong in hooks (we’ll get there).

I learned this the expensive way: there’s a ceiling on how many instructions a model can actually track. My first CLAUDE.md was 500 lines. Claude ignored half of it. Past a couple hundred rules, things get dropped. If your CLAUDE.md reads like a legal contract, you’ve gone too far.

Claude already reads your code, your file structure, your config files. CLAUDE.md fills the gaps it can’t infer — the “why” behind weird patterns, the warnings, the non-obvious build commands.

Where CLAUDE.md files can live

  • ~/CLAUDE.md— global, applies everywhere
  • ./CLAUDE.md— project root, shared with your team via git
  • ./.claude/rules/*.md— split into focused topic files (the Fort leans on this heavily)

The rules directory saved me. Instead of one massive CLAUDE.md, I split into focused files — guardrails.md, output-style.md, tool-routing.md. Claude loads them all, but I edit them independently. Keeps things from turning into a monolith.

Stop approving the same thing

After a week of approving cat and ls fifty times a session, I found two ways out:

1. Per-session.When Claude asks permission, choose “always allow” — that tool pattern is clear for the rest of the session.

2. Settings file. Create .claude/settings.local.json in your project:

json
{
  "permissions": {
    "allow": [
      "Bash(cat:*)",
      "Bash(ls:*)",
      "Bash(git status:*)",
      "Bash(git diff:*)",
      "Bash(npm test:*)",
      "Bash(npm run:*)"
    ]
  }
}

Pattern is Bash(command-prefix:*) where * matches any arguments. The Fort allowlists git operations, docker compose, ssh, rsync— hundreds of patterns built up over months. That’s the difference between Claude asking you 40 questions per task and Claude just.. doing the work.

The security tradeoff is real though.Every allowlisted command is one Claude can run without asking. I started conservative. Expanded as trust built. The Fort’s allowlist grew over months, not in an afternoon.

Spawning helpers

Claude can spin up sub-agents — independent instances that go do a thing and come back with a report. I use these for three things:

  • Research that would clutter the conversation. “Go figure out how the auth flow works and report back.” The sub-agent reads files, traces code paths, returns a summary. Main chat stays clean.
  • Background work.Long-running analysis, data processing, stuff I don’t need to watch happen.
  • Parallel tasks. Multiple agents working independent problems at the same time.

You can also define custom agents in .claude/agents/ with their own permissions, models, and instructions:

yaml
# .claude/agents/db-reader.md
---
name: db-reader
description: Execute read-only database queries
tools: Bash
model: haiku
---
You are a database analyst with read-only access...

This one uses Haiku — cheaper, faster — and I give it hooks that enforce read-only queries. Would I want my main agent running arbitrary SQL? No. A constrained sub-agent with guardrails? That I’m fine with.

Giving Claude new tools

MCP — Model Context Protocol — is how you extend Claude beyond files and bash. An MCP server hands Claude access to external stuff: databases, APIs, browser automation, Slack, Notion, whatever you need.

Adding an MCP server

bash
# Via CLI (recommended)
claude mcp add my-server --type stdio -- npx @my-org/mcp-server
json
{
  "mcpServers": {
    "github": {
      "type": "stdio",
      "command": "npx",
      "args": ["@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "your-token-here"
      }
    }
  }
}

The Fort connects to MCP servers for n8n workflows, Linear, Discord, YouTube transcripts, browser automation, a rendering engine — more than I’d have guessed when I started. But none of that was day one. I added each one when I hit a problem that needed it.

Common first MCP servers

  • GitHub— read issues, create PRs, review code
  • Postgres/SQLite— query your database directly
  • Brave Search / Exa— web search from inside Claude
  • Filesystem— extended file operations
  • Playwright— browser automation and visual testing

Gotcha:JSON doesn’t allow trailing commas. This is the number one MCP config mistake. Ask me how I know. Validate with jq . before restarting Claude.

Right now I have Claude check Linear, cross-reference GitHub, and draft release notes — three MCP servers, one prompt. Took months to get there. Worth every one.

Automating the boring stuff

Hooks are shell scripts that fire at specific moments in Claude’s lifecycle. Here’s why I care: CLAUDE.md instructions are probabilistic — Claude follows them most of the time. Hooks are deterministic. They run every time. No exceptions.

Key hook events

EventWhen It FiresUse Case
PreToolUseBefore Claude runs any toolBlock dangerous commands, enforce policies
PostToolUseAfter a tool completesAuto-format code, run linters, log actions
SessionStartWhen a session beginsLoad context, set environment variables
StopWhen Claude finishes a taskVerify completeness, queue follow-up work
PreCompactBefore memory compactionExtract insights before they’re lost

Real examples from the Fort

Block writes to .env files (hard deny, no wiggle room):

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "command": ".claude/hooks/guard-env-files.sh"
        }]
      }
    ]
  }
}

The script checks if the target is .env, credentials.json, *.pem, or *.key and returns a deny:

bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
BASENAME=$(basename "$FILE")

case "$BASENAME" in
    .env|.env.*|credentials.json|*.pem|*.key)
        echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Blocked: sensitive file. Manage manually."}}'
        ;;
esac
exit 0

Scan for hardcoded secretsbefore any file write: I built a hook that regex-matches content against API key patterns — OpenAI, AWS, GitHub, Stripe, Google — and blocks the write with a message showing the first 8 characters of the detected key. Catches the mistake at write time, not after the commit, not in CI.

Enforce conventional commits: A PreToolUse hook inspects git commit -m and checks the message against feat|fix|chore|docs|.... Doesn’t match? Prompts for confirmation instead of hard-denying — because sometimes you want to break the convention.

Save insights before compaction:When Claude’s context fills up and it needs to compact, a PreCompact hook scans the transcript for decision-language patterns and writes them to a file. Knowledge that would otherwise just.. vanish.

★ Insight

This was the hardest-won lesson in the whole setup. I had “NEVER write to .env files” in my CLAUDE.md for weeks. Claude followed it 95% of the time. The other 5% wrote an API key to a file that got committed. A 10-line shell script made it 100%. CLAUDE.md is for things Claude should know. Hooks are for things that must happen. That 5% gap between a suggestion and a guardrail — that’s where the real mistakes live.

Where config lives

Configuration ends up in a few places. Took me a bit to map it all:

FileScopePurpose
~/.claude.jsonUser-globalMCP servers, global preferences
.claude/settings.local.jsonProject, privateYour personal allowlists and permissions
.claude/settings.jsonProject, sharedTeam-wide settings (commit to git)
CLAUDE.mdProjectInstructions and context
.claude/rules/*.mdProjectFocused instruction files

The .local distinction matters. settings.local.jsonstays out of git — it’s yours. settings.json gets shared with the team.

Part 3Power Patterns

Teaching it your workflows

This is where things got interesting. Skills are markdown files that teach Claude specific workflows, loaded on demand with slash commands. Unlike CLAUDE.md — which loads every session — skills only eat context when you actually invoke them. That distinction matters way more than it sounds like it should.

The Fort has 40+ skills now. Daily rituals, deployment pipelines, code review, context switching, research capture — all invokable by name:

SkillTriggerWhat It Does
/ship“ship it”Review, verify, commit, push, PR, beads cleanup
/distillSession endExtract learnings into persistent memory
/pulse“quick check”Lightweight status scan (mail, workers, issues)
/switch“jump to X”Context switch between projects with state snapshot
/bod“morning”Beginning of day — load context, set focus
/eod“wrap up”End of day — log, reminders, distill
/review-prPR URLMulti-pass security + code review
/garden“clean up”Prune stale branches, aging issues, orphaned files

Creating your first skill

markdown
# ~/.claude/skills/deploy.md
---
name: deploy
description: Deploy the current project to production
user_invocable: true
---

# Deploy Skill

1. Run the test suite: `npm test`
2. Build for production: `npm run build`
3. If both pass, deploy: `rsync -avz ./dist/ user@server:/var/www/app/`
4. Verify the deployment: `curl -s https://myapp.com/health`
5. Report the result

That’s it. /deploy is now available in any session. Skills live in ~/.claude/skills/for global ones or your project’s .claude/directory for project-specific ones. Just markdown — but encoding your exact workflow, not a generic one.

30 focused lines loaded at the right moment beats 500 lines loaded every session. Claude performs noticeably better with focused context. The difference was.. immediately obvious.

Making it remember

This was the problem that kept nagging me. Every session started from zero. Same explanations. Same context-setting. Same mistakes rediscovered. Claude Code gives you a couple of building blocks — CLAUDE.md and auto-memory. The Fort layered more on top:

1. CLAUDE.md (built-in)

Loaded every session. Your project’s persistent brain. Keep it lean — this is prime real estate.

2. Auto-Memory (built-in)

Claude saves notes to ~/.claude/projects/<project>/memory/that persist between sessions. Small factual things — “the API rate limit is 100/min” or “Corey prefers tabs over spaces.” It proposes saves when it learns something useful.

3. Skills (built-in, content is yours)

Loaded via slash commands. Specialized knowledge that’s only relevant some of the time.

4. Custom memory system (Fort-built)

The built-in layers weren’t enough. I built a full Johnny Decimal-numbered memory system — each project and system gets a numbered file with operational details:

text
memory/
├── 50-infrastructure.md       # Server & Docker setup
├── 51-cli-tools.md            # CLI tools & scripts
├── 52-workflows.md            # Workflow automation
├── 53-claude-code.md          # Claude Code config
├── 55-notifications.md        # Notification systems
├── 60-home-dashboard.md       # Home project
├── 65-work-project.md         # Work project
└── ...

API quirks, deployment steps, architecture decisions, stuff learned the hard way — all in the right file. A routing table in MEMORY.md maps file paths to memory files, so when I start editing projects/home-dashboard/, the relevant context loads automatically. No manual prompting.

Research context degrades at compaction. I spent 30 minutes debugging an API quirk once, context compacted, and those findings just.. vanished. Had to rediscover the same thing the next session. Writing findings to a memory file immediately is the difference between learning once and re-learning the same lesson every time.

Working on two things at once

Switching branches in Claude Code nukes its accumulated understanding of your codebase. Annoyed me until I found worktrees — separate working directories, one per branch:

bash
# Native Claude Code worktree support
claude -w feature-auth

# Or manually
git worktree add ../my-project-feature-auth feature-auth
cd ../my-project-feature-auth
claude

Two Claude sessions. One on main, one on feature-auth. Each with full context of their branch. Neither pollutes the other.

When context runs out

Compaction and recovery

Claude has a context window. When it fills up, Claude compacts— summarizes what happened and forgets the details. Usually fine. Sometimes you lose something that mattered.

PreCompact hooks grab critical information before compaction happens. The Fort extracts decisions and insights from the transcript automatically.

Compaction recovery: I built a pattern where Claude writes an assistant-state.mdfile before compaction — what skill was running, what state the conversation was in. After compaction, it checks the file and picks up where it left off. Like a save point.

Session stream

Instead of relying on memory for everything, the Fort writes a log in real-time:

bash
fort-stream decision "chose FTS5 over external search" my-project
fort-stream deploy "dashboard to server" server docker-compose
fort-stream blocker "storage bucket not created" fort-p12y

Append-only. Timestamped. Tagged with context. These entries become the raw material for daily logs, weekly reviews, and actually knowing what happened across sessions.

Distillation

At session end, the Fort runs /distill— a background agent that reads the session’s changes, pulls out the learnings, writes them to the right memory files. This is how knowledge compounds. Without it, every session starts from zero. With it, each one builds on the last.

Key design decision: distill runs as a background agent, not in the main conversation. It makes dozens of Read/Grep/Edit calls that would clutter the chat. I see the summary. That’s it.

Without distill, session 47 has no idea what session 46 figured out. That’s the whole problem.

Part 4The Fort Blueprint

I made a folder in December, dropped a CLAUDE.md in it, and started working. No grand plan. I just got tired of re-explaining context every time I opened a new session. Five months of that and the folder grew teeth.

Everything has a place

text
claudes-fort/
├── .claude/
│   ├── hooks/              # 32 hook scripts
│   ├── rules/              # Focused CLAUDE.md fragments
│   ├── agents/             # Custom sub-agents
│   └── settings.local.json # Allowlisted commands + permissions
├── CLAUDE.md               # Project brain (kept lean)
├── memory/                 # JD-numbered persistent knowledge
├── plugins/
│   └── fort/
│       └── skills/         # 50+ slash commands
├── logs/                   # Daily session streams
├── notes/                  # Working notes, research, references
├── projects/               # Active project codebases
├── deploy/                 # Deployment configs
├── scratch/
│   ├── design-lab/         # Visual explorations
│   ├── playground/         # Prototypes
│   ├── scripts/            # One-off scripts
│   └── archive/            # Finished experiments
├── services/               # Local dev services
└── bin/                    # Custom CLI tools

Everything has a home. No loose files in scratch/— subdirectories. Group by project, not date. The directory structure isthe organization system. None of this was planned upfront — it emerged from five months of “where should this thing live?”

Steal these ideas

1. Some rules are suggestions, some aren’t

Hardest-won lesson in the whole setup. CLAUDE.md instructions are probabilistic— Claude follows them most of the time, not all of the time. Hooks are deterministic— they fire every time, guaranteed. Mix these up and you’re either over-engineering everything with hooks or trusting instructions for stuff that absolutely cannot fail.

Must happen every time: hook.

  • Secret scanning → hook
  • Commit message format → hook
  • Blocked file paths → hook

Guidance Claude should follow: CLAUDE.md.

  • Code style preferences → CLAUDE.md
  • Architecture decisions → CLAUDE.md
  • Workflow recommendations → CLAUDE.md

2. Issues that survive between sessions

I needed something that outlived the conversation. Beads is a lightweight issue tracker that lives in the repo — a .beads/directory. Issues persist across sessions and survive compaction. Conversation context gets summarized and compressed. Beads issues don’t. When context evaporates, a Beads issue is the lifeline telling the next session what to pick up.

The workflow intelligence layer checks beads before work starts (“is there already a ticket for this?”) and after it finishes (“should we close one?”). Keeps multi-session projects coherent without leaning on memory alone.

3. Recording what happened, automatically

Hooks handle the mechanical stuff — commits, session start/stop. Claude handles the semantic stuff — decisions, insights, blockers, deploys. Together they create a timeline of everything that happened, even across parallel sessions.

The stream feeds daily logs, weekly reviews, retros. It’s the difference between “I think we deployed that on Tuesday” and actually knowing when, what, and why.

4. Wiring events to the outside world

A SessionStart/SessionEnd hook watches for git commits, deploys, and skill invocations, then fires events to n8n and Fort Channel. Means:

  • Every commit triggers a webhook
  • Every deploy sends a notification to Discord
  • Every skill invocation gets logged

The code is simple — a curl to an n8n webhook, fire-and-forget with a 2-second timeout so it never blocks Claude:

bash
curl -s -X POST "$N8N_ENDPOINT" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD" \
  --connect-timeout 2 \
  --max-time 5 \
  > /dev/null 2>&1 || true

5. Giving it a personality

The Fort has a soul.md— not rules, but values. “Craft over speed.” “Honesty over compliance.” “Persistence over perfection.” Sounds fluffy until you see it affect behavior in ambiguous situations. When there’s no rule for what to do, the soul document guides the judgment call.

A companion working-with-corey.mdcaptures preferences learned over time — “concise over verbose,” “prefers persistent tracking over ephemeral notes,” “values iteration: start simple, prove it works, add complexity.”

Did I need this? No. Does it make the collaboration feel less like talking to a machine and more like working with someone who knows how I think? Yeah.

6. Loading context before you ask

A routing table maps file paths to memory files. I start editing projects/home-dashboard/ and Claude automatically loads memory/60-home-dashboard.md— deployment details, architecture notes, API quirks, lessons from previous sessions. All there before I think to ask for it.

The cost is maintaining the routing table and memory files. The payoff is enormous for projects I come back to often — Claude starts every session with relevant context instead of from zero.

Mistakes we made so you don’t have to

Gotchas

CLAUDE.md bloat kills effectiveness. Started with a 500-line CLAUDE.md. Claude ignored half of it. Split it into focused files under .claude/rules/, trimmed aggressively. The test for every line: “would removing this cause a mistake?”

Research context evaporates.Spent 30 minutes debugging an API quirk, context compacted, findings gone. Had to rediscover the same thing the next session. Then it happened again. The Fort’s workflow intelligence now prompts “Save these findings to memory?” after any real research. Built that after losing the same findings twice.

Hooks must be fast. A slow PreToolUse hook runs before everytool call. 500ms per hook, 50 tool calls per task — that’s 25 seconds of latency you just added. Keep hooks under 100ms. Fire-and-forget for anything async.

Permission scope creep is real.Every allowlisted command is an attack surface if someone tampers with your CLAUDE.md — prompt injection is a thing. Community skills and MCP servers are untrusted code. Audit what you install. Read the source. The Fort runs /security-audit periodically for exactly this reason.

Git push can hang forever. On macOS, git push triggers the osxkeychain GUI prompt, which blocks the terminal in an agent session. Indefinitely. Fix: set GIT_ASKPASS=echo GIT_TERMINAL_PROMPT=0 or use gh pr create instead. I lost an hour to this one.

Sub-agents start with nothing.When you dispatch work to a sub-agent, it has zero conversation context. You must include everything it needs in the prompt — it can’t see what you discussed in the main chat. I learned this when a sub-agent came back with a beautiful, thorough report about the wrong topic entirely.

Anti-patterns

Don’t over-automate. Tempting to hook everything, skill everything. But each automation is maintenance. Start with pain points, not cool ideas.

Don’t hand over the keys too early.The permission system exists for a reason. Build trust gradually. The Fort’s allowlist grew over months — started with maybe 20.

Don’t treat CLAUDE.md as documentation. It’s instructions, not a wiki. Claude doesn’t need your project’s history — it needs to know what to do and what not to touch.

Don’t skip the community.People are sharing skills, hooks, and configs. Before building your own, check if someone solved it. But — vet everything. Read the source. Trust boundary for community skills is basically zero.

Don’t fight compaction.It’s going to happen. Instead of trying to prevent it, build systems that survive it — memory files, session streams, beads issues. Anything that only lives in the conversation is going to disappear, so push it into files that don’t.

Start here, grow from there

Nobody starts with 32 hooks and 50 skills. Here’s roughly how mine grew:

Week 1:Installed Claude Code. Wrote a basic CLAUDE.md — build commands, code style, a couple gotchas. Used it for real work. Got a feel for where it was sharp and where it stumbled.

Week 2: Added settings.local.jsonwith permissions for my most-approved commands. Wrote my first hook — guard-env-files.sh, 30 lines, prevented a real mistake. Created one skill for my most repeated workflow.

Month 1: Built a memory system for my most active project. Added a session start hook that loads context. Tried worktrees for parallel work. Added one or two MCP servers that solved actual problems.

Month 2+:The rest emerged from daily use. I noticed patterns. Pain points. Little opportunities. Each one became a hook, a skill, a memory file, or a line in CLAUDE.md. None of it was planned up front — it just accreted from daily use.

The only rule: solve real problems, not hypothetical ones. Every piece of the Fort exists because something went wrong or was too slow without it. The best automation is the one you build after doing the thing manually for the third time.

I’m a designer who learned to code with Claude Code. Did I need to build all this? Probably not. But sometimes you just need to solve N+1. Start with a folder. Add a CLAUDE.md. The folder will grow teeth on its own.

Quick Reference

Essential commands

bash
claude                    # Start a session
claude -w branch-name     # Start in a worktree
claude --resume           # Resume last session
claude mcp add            # Add an MCP server
claude mcp list           # List configured servers
/help                     # In-session help
/skills                   # List available skills
/clear                    # Clear context

File hierarchy

text
~/.claude.json                    # Global config + MCP servers
~/.claude/skills/*.md             # Global skills
~/CLAUDE.md                       # Global instructions
./CLAUDE.md                       # Project instructions
./.claude/settings.json           # Project settings (shared)
./.claude/settings.local.json     # Project settings (private)
./.claude/hooks/*.sh              # Hook scripts
./.claude/agents/*.md             # Custom sub-agents
./.claude/rules/*.md              # Focused instruction files
./.mcp.json                       # Project MCP servers (shared)

Hook decision flow

text
PreToolUse hook returns:
  - exit 0, no output    -> allow (default)
  - exit 0 + deny JSON   -> block with explanation
  - exit 0 + ask JSON    -> prompt user for confirmation
  - exit 2               -> block silently

Started as a folder. Grew teeth. Current as of March 2026.

Post · Sawtooth Studio · March 2026