Skip to content

About

A template for building persistent AI workspaces on top of Claude Code

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

fort-starter — persistent AI workspaces

A template for building a persistent AI workspace on top of Claude Code. It gives an AI agent persistent memory, enforced rules, and specialized tools -- all inside a git repo that you control. It's not a framework. It's a workspace template you fork and make your own.

This was extracted from a personal setup that's been in daily use since December 2025. The patterns here aren't theoretical -- they've survived ten months of real work across dozens of projects, usually with several agent sessions running side by side.

A day with fort-starter: /bod picks up where you left off, several desks work on the Fort Board, /eod writes down what was learned, and the next morning's session already knows it
A day with fort-starter. Watch the 44-second explainer.

New in October 2026: run several sessions at once with /spawn, the Fort Board sidebar and quick replies. See Running several sessions at once and the changelog.

Heads up: this is a WIP and an ever-evolving project. The setup it was extracted from changes weekly, and this template tracks along with it. Pin a commit if you need stability.


Why infrastructure, not just a prompt

Claude Code reads a CLAUDE.md file at the root of your project and follows the instructions in it. This works surprisingly well -- about 95% of the time. That last 5% is the problem.

Instructions in CLAUDE.md are probabilistic. The model follows them with high reliability, but not certainty. Shell hooks are deterministic. They run every time, with no exceptions.

Here's a concrete example. You could put this in your CLAUDE.md:

## Security
- Never commit .env files or files containing API keys
- Always use specific file paths with git add, never git add .

And Claude will follow it. Almost always. But "almost always" isn't good enough for security. So instead, this workspace includes a shell hook:

#!/bin/bash
# guard-env-files.sh -- Blocks Write/Edit to .env, credentials.json, *.pem, *.key
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
BASENAME=$(basename "$FILE")
case "$BASENAME" in
    .env|.env.*|credentials.json|*.pem|*.key)
        # Return a hard deny -- Claude cannot bypass this
        echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny",
          "permissionDecisionReason":"Blocked: writing to sensitive file. Manage manually."}}'
        ;;
esac

The hook fires on every file write, every time, regardless of what the model decides. CLAUDE.md handles the 95% -- shaping behavior, setting tone, defining workflows. Hooks handle the 5% where failure isn't acceptable.

This distinction -- probabilistic vs. deterministic -- is the organizing principle behind everything in this repo.


Install

git clone https://github.com/Corey-T1000/fort-starter.git my-workspace
cd my-workspace
./fort-bootstrap --profile=vanilla

Then claude to start your first session. See Getting started below for what to do next.


Running several sessions at once

One session is a chat. The setup this came from usually has four to eight going: one per thread of work, each on its own branch. Three pieces make that manageable.

A cmux window with a Claude session on the left and the Fort Board on the right. A desk called elevation-cache, just spawned from route-planner, sits nested under it on the board. Click to watch the 71-second session
A real session in cmux, with made-up demo desks: /spawn a desk, answer another desk from the board, then /despawn merges the work back. Watch the 71-second session.

1. /spawn -- branch a thought into its own desk. Mid-conversation, a side idea shows up. /spawn auth-refactor writes a short brief from the current conversation (focus, files to read, constraints, what to deliver), creates worktrees/auth-refactor on branch spawn/auth-refactor, and opens a fresh Claude session there that starts from the brief. Your current session doesn't move. The new desk opens as a cmux workspace if you're in cmux, a tmux window if you're in tmux, and otherwise prints the command to paste. Set FORT_SPAWN_RC=1 and the desk also starts with Remote Control, so it shows up in the Claude app on your phone.

2. The Fort Board -- one column that says whose turn it is. A cmux sidebar (core/cmux/) lists every desk with its state:

State Meaning
Needs you blocked on a permission prompt
Your move the reply ends in a question, shown on the card
Cooking working
Done / Ready to close finished; "ready" once the session's memory capture has landed
Stalled / Waiting interrupted, or quiet for a while

cmux sidebars can't read files or the network, so the desk-status.sh hook publishes each desk's state as the workspace's progress label and the sidebar reads that. Optional: point FORT_GIST_URL at any Ollama-style endpoint and each card gets a one-line summary from a small local model. Leave it unset and the cards show the plain state plus the first line of the reply. Nothing breaks either way.

3. Quick replies -- answer without switching tabs. When a desk asks a yes/no question, its card gets yes / no chips; any open question gets ack to quiet it until the next turn. Sidebars can't type into a terminal, so a chip leaves a note on the desk and a small relay (bin/fort-board-relay, a launchd agent) types the reply in. Fixed replies only.

When a desk is done: /despawn auth-refactor runs the memory capture, then merges the branch back and removes the worktree. It previews first, refuses uncommitted work or a merge conflict, and never pushes.

Setup for the board and relay: core/cmux/README.md. Spawn and despawn work without cmux.


The five layers

The five layers stacked like the logo: Config and Hooks at the base, Memory and Skills above them, CLI on top. Click to watch the explainer

                               ⚑
                               │
┌─────────────────────────────────────────────────────────────┐
│  5. CLI            bin/                  Shell utilities     │
├─────────────────────────────────────────────────────────────┤
│  4. Skills         .claude/skills/       Slash commands      │
├─────────────────────────────────────────────────────────────┤
│  3. Memory         memory/ + MEMORY.md   Persistent context  │
├─────────────────────────────────────────────────────────────┤
│  2. Hooks          .claude/hooks/        Deterministic rules  │
├─────────────────────────────────────────────────────────────┤
│  1. Config         CLAUDE.md + rules/    Identity & behavior  │
└─────────────────────────────────────────────────────────────┘

The logo is the stack. Five pieces, darkest at the foundation, lightest at the top. The door is an invitation.

1. Config -- CLAUDE.md + rules/

Your CLAUDE.md is the agent's identity document. It tells Claude who it's working with, how to communicate, what workflows exist, and where things live. The rules/ directory breaks this into focused files (output style, tool routing, guardrails) that Claude Code loads automatically. Think of it as the probabilistic layer -- behavioral guidance that shapes 95% of interactions.

2. Hooks -- .claude/hooks/

Shell scripts that Claude Code executes at defined trigger points: before a tool runs (PreToolUse), after it runs (PostToolUse), or when a session ends (Stop). Each hook returns a JSON decision -- allow, deny, or ask (prompt the user). The starter includes ~23 hooks across security, workflow, and quality categories. The setup this was extracted from runs 42.

3. Memory -- memory/ + MEMORY.md

Topic files organized with Johnny Decimal numbering. MEMORY.md is a routing table that maps file paths to memory files -- when you start editing files in projects/dashboard/, the agent automatically loads memory/60-dashboard.md with prior context. Memory files hold operational knowledge: architecture decisions, API gotchas, deployment details. Knowledge that would otherwise be lost at the end of every session.

4. Skills -- .claude/skills/

Each skill is a directory, .claude/skills/<name>/SKILL.md, and the directory name is the slash command: /distill, /research, /spawn, /eod. A skill is a structured prompt Claude loads on demand (not on every session), so specialized workflows stay out of your base context window. Supporting files (templates, references) live in the same directory. Sources are in core/plugins/*/skills/ and profiles/vanilla/skills/; fort-bootstrap installs 28 skills into .claude/skills/. (See "The skill stack" below for the full taxonomy and how they compose.)

5. CLI -- bin/

Shell utilities that support the workspace from outside Claude Code. Status dashboards, notification wrappers, memory linting, session streaming. These run in your terminal, in cron jobs, or get called by hooks. They're the glue between the AI workspace and your existing dev environment.


Receipts — ten months in

The setup this was extracted from, today:

Thing Count
Hooks running 79 (security / workflow / quality)
Memory files 144 JD-indexed topics · 777 session narratives · 325 feedback files
Skills invokable 336
Commits since Dec 2025 2,393
Session logs captured 240
Semantic stream entries (decisions / deploys / ships / research captured via fort-stream) 1,870
Worktrees on disk 64 (most are parked desks from /spawn)

Last measured 2026-10-16 · regenerate via bin/fort-receipts

The starter ships lighter on purpose: 26 hooks, 28 skills, 4 worker agents. It grows with you, not at you.

Model routing is a cost lever. The worker agent profiles in core/agents/ route mechanical work (file search, status checks) to a small model and keep the large one for review and ambiguous calls. On a subscription that shows up as headroom (more parallel sessions before you hit usage limits); on API billing it's a direct discount on that slice of the bill. Current per-model prices: anthropic.com/pricing.


What it actually looks like

Setup

$ git clone https://github.com/Corey-T1000/fort-starter.git my-workspace
$ cd my-workspace
$ ./fort-bootstrap --profile=vanilla

🏰 Fort Setup

━━━ Assembling Fort for vanilla ━━━

  ✓ CLAUDE.md assembled (base + vanilla)
  ✓ bin/ scripts installed
  ✓ Hooks installed: 26 active, 0 disabled
  ✓ Rules installed (4)
  ✓ Agents installed (4)
  ✓ Settings installed from vanilla profile (paths resolved)
  ✓ Plugins installed: 2
  ✓ Skills installed: 28 (.claude/skills/<name>/SKILL.md)
  ✓ Manifest written (fort-manifest.json)

  Ready. Run `claude` to start your first session.

Memory auto-loading

$ claude

> I need to update the dashboard's chart component to handle empty datasets

  Loaded memory for dashboard (60).

  Note from memory: the chart library silently drops data points over 10k rows.
  Your empty dataset case might hit the same codepath. Let me check.

Hook blocking a mistake

> Let me commit these changes

  $ git add .

  Hook: guard-git-add.sh
  Broad git add detected. This could stage .env files, credentials,
  or large binaries. Consider adding specific files instead.

  Good catch. Let me add the specific files instead.
  $ git add src/components/Chart.tsx src/lib/data.ts

Flows — real sessions that show the value

Eight scenarios, each showing a specific value prop. Expand the ones that interest you.

Flow 1 — Memory auto-load saves the re-learning tax

Situation: opening a session on a project i havent touched in 3 weeks. the chart component needs an update.

Flow:

$ claude
> hey need to update the chart on the dashboard

  Loaded memory for dashboard (60).
  Note from memory: chart library silently drops data points over 10k rows.
  Also: CSS grid gap breaks in the kiosk browser — use margin.

Payoff: no re-learning. the two gotchas that wouldve cost me 45 min of "wait why is this broken" are loaded before i type anything.

Flow 2 — Worker agent routing keeps a research-to-ship session efficient

Situation: building a new section for the marketing site. need competitive research, then design exploration, then implementation, then review.

Flow:

> research how other permission-system companies handle first-session
  onboarding

  [dispatches worker-research on Sonnet]
  [returns 15 min later with notes at scratch/research/onboarding-comp.md]

> /design-lab for 5 variations of the onboarding hero

  [dispatches 5 variations to scratch/design-lab/]
  [pick winner]

> /brainstorming → /writing-plans → /executing-plans

  [editor worker on Sonnet scoped to projects/web/]

> /review-pr

  [4-pass review, reviewer on Opus]
  [flags one a11y issue, one copy nit]

> /ship

Payoff: mechanical lookups hit Haiku (~15× cheaper per token than Opus). editor on Sonnet (5× cheaper). only the reviewer burns Opus, and only for the ~10 min review window. on Max the savings show up as headroom; on API as a ~10× bill cut for the same output.

Flow 3 — The recurring gotcha that graduated into a hook

Situation: kept having claude commit directly to main instead of the feature branch when working in worktrees. added a CLAUDE.md instruction. it got ignored ~1 in 8 times anyway.

Flow:

[week 1] "always check you're on the right branch before commit"
         in CLAUDE.md
[week 2] it happened again
[week 3] it happened AGAIN
[week 3] wrote guard-worktree-branch.sh:
         - PreToolUse hook on Bash
         - if command matches `git commit` AND cwd is a worktree
           AND HEAD is main/master → deny with message
[week 4+] never happened again

Payoff: CLAUDE.md was saying "dont do X" and ~1/8 times it did X anyway. the hook made "dont do X" into "cant do X." havent force-pushed to main since.

Flow 4 — Research once, benefit for months

Situation: spent 2 hours reverse-engineering a sketchy vendor API. cookies, session tokens, undocumented endpoints.

Flow:

[session 1]
> /research the vendor API auth flow

  [worker-research dispatches, 90 min of back-and-forth]
  [findings land in scratch/research/vendor-api.md]

> /note save these findings

  "Save findings? Routes to memory/67-vendor-apis.md (JD 67)."
  [yes]

[3 months later, new session touching projects/vendor-client/]

  Loaded memory for vendor-apis (67).
  Auth flow: login → /api/v2/sessions → cookie → CSRF header required.
  Gotcha: session expires silently after 20 min, re-auth without prompt.

Payoff: three months later, starting a new integration, i dont re-derive any of it. the silent-expiration trap especially — if i hit that fresh it wouldve been another long afternoon.

Flow 5 — Design lab for UI direction

Situation: tasked with refreshing a page but not sure which direction to go. i have opinions but want to see variations before committing.

Flow:

> /design-lab for the homepage hero — 5 directions, varied

  [generates 5 HTML variations in scratch/design-lab/homepage/]
  [each has a different typographic + layout approach]

> [open all 5 in browser, compare]
> "i like #3's typography and #1's layout combined"

> /design-lab synthesize 3+1

  [generates a synthesis, writes an implementation plan]

> /executing-plans

Payoff: got to see a few directions before committing. if id just built what came to mind first id have landed somewhere safe, fine, forgettable. this way i have the ability to Nx my concepts in and find novel ideas.

Flow 6 — Multi-pass review catches what one pass missed

Situation: solo project, no team review. built a new sync pipeline. want to ship but unsure of quality.

Flow:

> /review-pr

  [dispatches 4 specialized agents in parallel:]
  [- security-auditor on Opus: checks secrets, injection, permissions]
  [- code-reviewer on Opus: correctness, style, patterns]
  [- verifier on Sonnet: runs tests, lints, type-checks]
  [- context-checker on Sonnet: reads memory for this area, flags drift]

  [synthesizes report — 1 critical, 3 suggestions]
  [critical: race condition in the token refresh, exact file:line]

> fix critical, suggestions deferred

> /ship

Payoff: caught a race condition in the token refresh that i never wouldve spotted. four agents looking at the same code from different angles, one found it. costs more than skipping review, costs way less than a prod race condition.

Flow 7 — The compounding payoff at month 5

Situation: month 5 of the setup. starting a new feature on a project i havent touched in 10 weeks.

Flow:

> claude
> need to add a new sync strategy to the pipeline

  Loaded memory for data-pipelines (57).
  Recent decisions: 15min poll interval, exponential backoff on 429s.
  Active bug: timestamp field gets truncated to seconds in postgres —
   use stored_at_ms for millisecond precision.
  Known gotchas: 8 listed.

Payoff: "new session on old topic" used to feel like "oh no, i have to page everything back in." now it feels like "cool, what are we doing today." five months in, this is the biggest shift in how i work.

Flow 8 — /distill catches what i didnt know was worth saving

Situation: end of a long debugging session. fixed the bug, exhausted.

Flow:

[stop hook detects session ended without /distill, queues for next session]

[next session start]
  Previous session queued distill.
  [runs /distill as background agent]

  Captured to memory/57-data-pipelines.md:
  - the scraper was dropping rows silently when upstream API returned 429
  - root cause: retry loop didnt log exhaustion
  - fix: alert on all-keys-exhausted (commit abc1234)
  - gotcha: exhaustion alerts once per container lifecycle, not per run

Payoff: i wasnt gonna remember any of that. the commit has the fix. the WHY — "silent drop" as a signature, the specific retry loop behavior — wouldve been gone by next month. distill caught it without me having to sit down and write a postmortem.


Why this doesn't feel heavy in daily use

42 hooks, 87 memory topics, a kernel-level sandbox, a routing table that auto-loads context on file paths, workers dispatching on three model tiers. youd expect friction. there isnt any — because each layer is designed to stay out of the way unless failure would matter.

  • Single user, local first. the fort runs on my machine. blast radius is me.
  • Git is the safety net. every autonomous action lands in a diff i can see and revert. hooks never auto-commit to main, agents never push without my hand on the trigger.
  • Layered trust, anchored in SpiceBox. three fences — tool scope at the agent layer, hooks at the intercept layer, SpiceBox at the kernel layer. SpiceBox is the actual trust boundary; hooks handle behavior and workflow above it. before SpiceBox, hooks carried the whole trust story — which worked, right up until a pattern had a gap.
  • Observable. statusline shows cost live. audit trails capture what happened. Fort Board surfaces every parallel session.
  • Failures are loud. silent failure is the enemy — any autonomous step that fails pings Discord, watchdogs escalate, no-ops show up in status.
  • Right-sized stakes. nobodys healthcare depends on this. a scheduled job missing a run is annoying, not catastrophic. the autonomy envelope matches the consequences.
  • The honest risk. a dim factory trusts its envelope. if the envelope cracks (a hook silently breaks, a scope gate has a bug) damage can happen fast because no ones watching. thats why the observable layer is load-bearing — without it you cant run dim at all.

Deep dives

Three patterns worth stealing — hook taxonomy, memory routing, probabilistic vs. deterministic

Even if you don't use this template, these patterns transfer to any AI coding setup.

Pattern 1: Hook taxonomy

Not all hooks serve the same purpose. Categorizing them by intent makes the system easier to reason about and extend.

Security hooks -- Hard deny. Cannot be bypassed.

Hook What it does
guard-env-files.sh Blocks writes to .env, credentials.json, *.pem, *.key
guard-secrets.sh Scans file content for API key patterns and blocks the write
guard-git-add.sh Flags git add . and git add -A -- forces specific file staging

Workflow hooks -- Automation. Silently modify or augment agent actions.

Hook What it does
enforce-draft-pr.sh Injects --draft into every gh pr create command
format-commit-msg.sh Validates conventional commits format

Quality hooks -- Catching mistakes before they ship.

Hook What it does
guard-push-rebase.sh Blocks push if your branch is behind remote
stop-lint-check.sh Runs linter on recently modified files before session ends

The mental model: if you'd be upset when it fails, make it a hook.

This workspace uses SpiceBox alongside these hooks. Hooks handle the behavioral side -- what the agent should and shouldn't do. SpiceBox handles the permission side -- what the agent can and can't do at the OS and network level. Different layers, same goal.

Pattern 2: Memory routing

The memory system solves a specific problem: Claude Code sessions are stateless. Every new session starts fresh. Memory routing makes context loading automatic instead of manual.

MEMORY.md contains a routing table:

| Path prefix              | Memory file            | Tab title           |
|--------------------------|------------------------|---------------------|
| projects/dashboard/      | memory/60-dashboard.md | workspace:dashboard |
| projects/api/            | memory/61-api.md       | workspace:api       |
| deploy/                  | memory/50-infra.md     | workspace:infra     |

When the agent first edits a file matching a path prefix, it loads the corresponding memory file:

# 60 -- Dashboard

## Known gotchas
- The chart library silently drops data points over 10k rows
- CSS grid gap doesn't work in the kiosk browser -- use margin instead

## Recent decisions
- 2025-03-15: Switched from polling to SSE for live updates (reduced CPU 40%)

How to write good memory files:

  • Record the "why," not just the "what." Future sessions can read the code for "what."
  • Capture gotchas -- the things that cost you 30 minutes to debug once and would cost 30 minutes again.
  • Include dates on decisions so you know how stale the context is.

Pattern 3: Probabilistic vs. deterministic

The decision framework for everything in the workspace:

If failure means... Then use... Example
Nothing serious CLAUDE.md instruction "Use conventional commit messages"
Annoying but recoverable Hook with ask "Confirm before deploying"
Genuinely bad Hook with deny "Never write API keys to files"

Rules of thumb:

  • Security is always a hook. No exceptions.
  • Style and tone are always CLAUDE.md. Hooks can't judge prose quality.
  • If you find yourself repeating a CLAUDE.md instruction because it keeps getting ignored, that's a sign it should be a hook.
Worker agent profiles — cost discipline through model routing

This is the single biggest unlock in the setup this was extracted from, and it directly answers the "this workflow burns tokens" concern.

Claude Code lets you define named sub-agents with specific models and tool scopes. The starter ships four:

Agent Model Tools Dispatch for
worker-mechanical Haiku Read, Grep, Glob, Bash Fact lookups, file existence, config values
worker-research Sonnet +Write, WebSearch, WebFetch Investigation, competitive research, codebase exploration
worker-editor Sonnet +Edit, Write Scoped code changes from a brief
worker-reviewer Opus Read, Grep, Glob, Bash Senior review, security audit, pre-merge verification

Why this matters for cost:

A naive setup runs every sub-agent on the coordinator's model. If your coordinator is Opus, every "what port does this service use?" lookup burns Opus tokens. That's 10-20x more expensive than Haiku for a task Haiku nails.

Routing by job keeps the bill honest:

  • Mechanical (Haiku) — cheap and fast, for tasks where the answer shape is "a fact"
  • Research / Editor (Sonnet) — the workhorse tier, good judgment at a reasonable rate
  • Reviewer (Opus) — reserved for senior-level review where cost is worth it

Why this matters for safety:

Each agent's tool list is a scope gate. The reviewer can't write files — the definition doesn't include Edit or Write, so the tool isn't available at all. The editor can't run web searches. The mechanical worker can't touch the internet. Behavior-by-construction, not behavior-by-instruction.

Pair this with SpiceBox for OS-level permission enforcement (different filesystem scopes per agent, network allowlists, sandboxing) and you get defense-in-depth without writing a single hook.

Where they live:

core/agents/*.md. Bootstrap copies them to .claude/agents/. Edit, add, or delete — the four here are starting points, not prescriptions.

The compounding loop — a workspace that teaches itself

The infrastructure above is the scaffolding. What makes the setup actually compound over time is a handful of skills that feed signal back into the system. The Fort is semi self-improving — every session leaves the next session smarter.

A typical session generates a lot of signal: decisions, gotchas, research findings, surprises. Without a capture layer, that signal evaporates when the session closes. The "Knowledge capture" skills in the section below exist to turn sessions into durable context. Memory then loads on demand via the routing table (see Pattern 2 above). Next session's context window is pre-loaded with everything relevant before you type a word.

The loop has two tiers:

  1. Knowledge loop (fast). Research → capture → memory → auto-load next session. Days.
  2. Infrastructure loop (slow). When a CLAUDE.md instruction keeps getting ignored, promote it to a hook. When a multi-step workflow stabilizes across several sessions, extract it into a skill. When a failure mode bites twice, write a memory entry with the fix. Weeks to months.

Five months in, the compound is real. New sessions on known topics start productive in seconds because the gotchas, decisions, and architectural patterns are already loaded. The template you're looking at is itself the output of that loop — patterns that earned their way in by proving useful across dozens of real projects.

Your fork will do the same for your work.

Memory architecture — how the loop actually moves data

The compounding loop above is the user-facing story. Under it sits a compiler-style pipeline that moves session signal into queryable knowledge. Useful to understand because it tells you where to look when something seems lost, and where to add a stage when you outgrow the defaults.

   source                compiler              wiki              index             linter             archive
┌──────────┐         ┌──────────────┐      ┌──────────┐      ┌──────────┐      ┌──────────┐      ┌──────────┐
│  logs/   │         │  /distill    │      │ memory/  │      │MEMORY.md │      │ memory   │      │ your     │
│  session │ ──────► │  +           │ ───► │ XX-*.md  │ ───► │ routing  │ ───► │ hygiene  │ ───► │ durable  │
│  digest  │         │  /narrate    │      │ session_ │      │  table   │      │  checks  │      │ synthesis│
└──────────┘         └──────────────┘      │  *.md    │      └──────────┘      └──────────┘      │  layer   │
                                            └──────────┘                                          └──────────┘
                                                 ▲                                                     ▲
                                                 │                                                     │
                                            queried at runtime                                    optional, point
                                            via Read / Grep / `/search-fort`                      `$FORT_KNOWLEDGE_BASE`
                                                                                                  at your KB

Stages:

  1. Source — logs/YYYY-MM-DD.md (commit + session events from hooks) + per-session digests + the raw transcript.
  2. Compiler — dual extraction. Two complementary skills run on session end:
    • /distill reads files + git + the session log and writes operational facts into JD-numbered topic files.
    • /narrate reads the transcript itself and writes the conversational arc — implicit acceptances, one-line reframings, rejected alternatives — into memory/session_<date>_<slug>.md. Both fire because each catches what the other misses. /distill alone loses the reasoning trail. /narrate alone loses the file-level deltas.
  3. Wiki — memory/XX-topic.md (operational, JD-indexed) and memory/session_*.md (narratives, flag-tagged). What future sessions actually query.
  4. Index — MEMORY.md is the routing table. First file edit on a matching path auto-loads the relevant memory. No vector DB required at the default tier.
  5. Linter — periodic memory hygiene checks (broken refs, orphaned files, stale topics) keep the wiki query-clean. Run via /garden.
  6. Archive — your durable synthesis layer. The starter doesn't bundle one; if you keep an Obsidian vault or similar, point $FORT_KNOWLEDGE_BASE at it and the cross-check rule in rules/workflow-intelligence.md will use it.

AAAK Flags — making load-bearing moments findable.

Session narratives carry a Flags: [...] frontmatter field with a closed vocabulary:

  • PIVOT — course change
  • DECISION — load-bearing choice with rationale
  • ORIGIN — first-mention of a pattern that becomes load-bearing
  • CORE — fundamental concept underlying downstream decisions
  • SENSITIVE — sharp-edge moments (mistakes, retros, auth/PII)

The shape lets you ask "show me every PIVOT in the last month" instead of grepping prose for tone shifts. Closed set on purpose — every new flag dilutes the signal.

Retrieval — three layers, escalate by question shape

Question shape Reach for Why
Crisp keyword, known topic /search-fort (Read / Grep) Sub-100ms over local memory files
Vague, vibe-recall, "that session where..." /recall Agent-as-retriever scans narratives, filters by Flags
Outgrown agent-scan budgets (hundreds of narratives) Vector store (e.g. MemPalace) Optional capstone — see below

Capstone — when /recall stops scaling. If your narrative corpus outgrows what an agent can scan in a single dispatch, layer a vector-backed semantic store on top (for example ChromaDB with a scheduled re-index and Flag-aware filtering). Not bundled here: it's a Python venv plus a few hundred MB of dependencies, which is the right tradeoff at scale and the wrong one at zero.

The default starter retrieval — /search-fort for crisp keywords, /recall for vibe queries — is enough for the first several months of use. Graduate when you actually feel the budget, not before.

The skill stack — core verbs and how they compose

Skills are slash commands that wrap a specific workflow. The starter ships a bunch — grouped below by when they fire.

Daily rhythm

Skills that bracket the day. These set and close context so sessions start and end cleanly.

Skill When it fires What it does
/bod start of day Reads last session log, recent git, open PRs, parking-lot. Sets focus for the day.
/pulse mid-session breaks Lightweight status check — mail, workers, reminders. One-line summary.
/briefing "catch me up" Longer rollup across all persistence layers when returning from time away.
/eod end of day Reviews the day, writes daily log, surfaces tomorrow's focus, runs /distill.

Work skills (divergent → convergent)

Skills that do the actual work.

Skill Phase What it does
/research explore Dispatches worker-research to investigate a topic, writes structured findings to scratch/research/.

The vanilla profile keeps the work-skill set narrow on purpose — /research plus the daily-rhythm and capture skills cover the core loop. Other workflow skills referenced in flows below (/brainstorming, /writing-plans, /executing-plans, /design-lab, /review-pr, /ship) are part of the personal setup and are not bundled in vanilla. Add them as .claude/skills/<name>/SKILL.md directories in your fork.

Parallel desks

Skill What it does
/spawn Writes a brief from the current conversation, creates a worktree + branch, opens a new session there seeded with the brief.
/despawn Captures the desk's session, merges its branch back (previews first, never force, never pushes), removes the worktree.

Knowledge capture (the compounding loop)

Skills that feed signal back into memory — the layer that turns sessions into durable context.

Skill What it captures
/note Captures mid-session observations AND structured research findings → memory, BigBrain, notes, or scratch (auto-routed). Replaces the older /capture which folded into /note.
/park "Not now but later" → parking-lot
/distill Session-end extraction → JD-numbered memory (file-level signal)
/narrate Session-end extraction → memory/session_*.md (transcript-level signal: reframings, pivots, AAAK Flags)
/retro Post-incident deep zoom — what happened, what surprised, what to change
/garden Periodic maintenance — stale memory, orphaned scratch, broken refs

/devlog and /weekly-review referenced elsewhere in the docs are not bundled in vanilla — add via fork or wait for upcoming PRs.

/assistant — the persistent dispatcher

/assistant is the one that ties it all together. It's not a task runner, it's a conversational partner that holds the thread while routing work out to everything above.

What it does:

  • Keeps the main conversation responsive — heavy lifting goes to sub-agents via dispatch
  • Routes casual-language intent to the right skill ("check my calendar" → /calendar, "what's the status" → /pulse)
  • Writes a per-assistant state file to scratch/assistants/<slug>.md so focus survives compaction
  • Announces what skill it's reaching for before dispatching ("reaching for /research here")

Different modes it runs in:

  • Dispatch mode (default). You talk, it routes. Calls /research / /note on your behalf and results come back into the conversation.
  • Task-taker mode. When a brain dump starts — "oh also i need to..." items — it captures to notes/task-dump.md with timeline buckets (Now / This Week / Later / Someday) instead of derailing focus.
  • Multi-assistant. Supports 2-4 parallel /assistant sessions — one focused, one always-on for random asks, a third while waiting on sub-agents. Each is keyed by focus slug. No cross-session bleed.
  • Named resume. /assistant dashboard re-enters the named assistant at scratch/assistants/dashboard.md with full prior context. Ultra-light if < 4hrs old, standard resume otherwise.

How the skills interact through /assistant:

               ⚑
               │
   ┌─ /bod ────┴──────────── sets daily context
   │
   └─► /assistant ──────────── you talk to this all day
          │
          ├─ routes to ─► /research     (dispatch to worker-research)
          ├─ routes to ─► /pulse        (lightweight status check)
          ├─ routes to ─► /briefing     (longer rollup, "catch me up")
          ├─ routes to ─► /switch       (context switch between projects)
          │
          ├─ captures  ─► /note / /park (mid-thread)
          ├─ tracks    ─► task-dump (brain dump capture)
          │
   ┌─────┘
   │
   └─► /eod → /distill ────── closes the session, writes to memory
          │
          └─► next session starts with that memory auto-loaded

Flows below also show /design-lab, /review-pr, /ship, and the /brainstorming → /writing-plans → /executing-plans chain. Those skills are not bundled in the vanilla profile — they live in the upstream Fort and may land in future PRs. The flows are kept here as illustrations of how /assistant can route once you add them.

The whole point of /assistant is that you don't have to remember which skill to reach for. Say what you want in natural language and it picks the right one (or asks if it's ambiguous). The skills are the individual tools, /assistant is the one that knows which tool fits the job.


Getting started

1. What to do first

Create your first memory file. Pick a project you're actively working on. Create memory/60-your-project.md and write down three things: what the project is, one architecture decision you've made, and one gotcha you've hit. Add a route to MEMORY.md. Now every future session that touches that project starts with context.

Customize your CLAUDE.md. The vanilla profile gives you a minimal starting point. Add your communication preferences, your workflow conventions, your project structure. This is the single most impactful file in the workspace -- spend time on it.

Write your first hook. Start with something simple. Copy an existing hook from .claude/hooks/, modify the pattern matching, and change the response message. See the Claude Code hooks documentation for the full API.

Explore the skills. Type / in Claude Code to see available slash commands. Try /research for deep investigation, /distill at the end of a session to capture what you learned.

2. Growing the workspace

  • Week 1: Customize CLAUDE.md, create 2-3 memory files for active projects
  • Week 2: Write your first custom hook, start using /distill to capture session learnings
  • Month 1: Build project-specific skills, establish your memory routing table
  • Month 2+: The workspace reflects how you think. New sessions start productive immediately because the memory system carries forward what matters.

Project structure

The repo ships sources under core/ and profiles/. Running fort-bootstrap assembles those into the live .claude/ (hooks, rules, agents, skills/<name>/SKILL.md), plugins/, memory/, and bin/ directories your sessions actually use. ./fort-bootstrap --refresh re-applies sources after a git pull and only replaces skills the template ships, so skills you write yourself survive.

my-workspace/
├── CLAUDE.base.md             # Base CLAUDE.md template (copied to CLAUDE.md by bootstrap)
├── fort-bootstrap             # Setup script: assembles core + profile → live workspace
├── core/                      # Canonical sources (don't edit live copies — edit here)
│   ├── agents/                # Worker agent profiles (model routing + tool scoping)
│   ├── bin/                   # Shell utilities (includes statusline.sh)
│   ├── hooks/                 # Shell scripts -- deterministic enforcement
│   ├── plugins/               # Skill sources: core/plugins/<plugin>/skills/<name>/SKILL.md
│   └── rules/                 # Focused instruction files (auto-loaded)
├── profiles/
│   └── vanilla/               # Minimal starter profile
│       ├── CLAUDE.md          # Profile-specific CLAUDE.md addendum
│       ├── settings.json      # Profile-specific settings
│       ├── skills/            # Profile-only skills (10 in vanilla); same name overrides core
│       ├── skills-disabled/   # Per-profile skill opt-outs (extension point)
│       ├── plugins/           # Per-profile plugin overrides (extension point)
│       └── hooks-disabled/    # Per-profile hook opt-outs (extension point)
├── memory/                    # Persistent knowledge (JD-numbered topic files)
├── notes/                     # Scratch notes, parking lot
├── logs/                      # Session logs (auto-generated)
├── projects/                  # Your active codebases
└── assets/                    # Logo and brand assets

Customizing your profile

Three extension points let you adapt a profile without touching core/:

  • profiles/<name>/skills/<skill>/SKILL.md — a profile skill with the same name as a core one wins at install. profiles/<name>/skills-disabled/<skill> — touch it to skip a core skill.

  • profiles/<name>/plugins/<plugin-name>/ — drop a plugin subdirectory here to override or extend the same-named plugin under core/plugins/. During bootstrap, profile plugins win over core plugins of the same name. Use this when you want a profile-specific variant of a shared plugin.

  • profiles/<name>/hooks-disabled/<hook-name>.sh — touch an empty file with the same name as a core/hooks/<hook-name>.sh file to opt this profile out of that hook. Bootstrap skips any hook that has a matching disabled marker. Use this when a core hook doesn't fit the profile (e.g., a domain-specific hook that doesn't apply).

Both are searched at bootstrap time — no further wiring needed. Add files, re-run ./fort-bootstrap --profile=<name>, done.


FAQ

How is this different from just writing a good CLAUDE.md? A good CLAUDE.md gets you 80% of the way. The remaining 20% -- security enforcement, automatic memory loading, session-to-session knowledge transfer, workflow automation -- requires infrastructure around it.

Does this work with other AI coding tools? The concepts (persistent memory, hook-based enforcement, skill-based workflows) are transferable. The implementation is specific to Claude Code's hook system and CLAUDE.md conventions.

How much does this cost in context window? CLAUDE.md and rules load on every session -- budget ~2-3k tokens. Memory files load on demand. Skills load only when invoked. Designed to minimize baseline context usage.

What about overall token usage? This is a token-heavy workflow. Sub-agent dispatch, automatic memory loading, and session-end /distill cycles all spend tokens -- a session that fans out two or three parallel research agents can easily 5-10x a chat-style session. Plan accordingly: Claude Max's higher tier for daily use, or watch your billing if you're on the API. The tradeoff is intentional -- the template optimizes for depth and continuity, not minimum spend.

Three things keep costs honest:

  • Worker agent profiles with model routing -- see the "Worker agent profiles" section above. Routing mechanical work to Haiku and reserving Opus for senior review is the single biggest cost lever in the setup.
  • Bundled statusline -- core/bin/statusline.sh ships with the template and is wired up by fort-bootstrap. It renders a live two-line bar: context %, token count, and working directory on top; model, session duration, and accumulated cost on the bottom. Color tiers (green → amber → red) trigger at thresholds so you catch runaway sessions before the bill lands.
  • Claude Usage Tracker -- separate project by @hamed-elfayome that gives you longitudinal usage reports (per session, per day, per model). Good complement to the in-session statusline when you want a weekly rollup.

Can I use this for a team? Yes. The repo is git-based, so team conventions, shared hooks, and common memory files work naturally with branches and pull requests.

What's Johnny Decimal? A numbering system for organizing topics. Each category gets a two-digit number (50-59 for infrastructure, 60-69 for projects). See johnnydecimal.com.


Related projects

  • SpiceBox -- Fine-grained permissions and sandboxing for Claude Code. If fort-starter is the workspace, SpiceBox is the security perimeter: hook-based permission enforcement, macOS sandbox profiles, and network-level controls. Complementary approaches to the same goal of making AI coding agents production-grade.
  • Claude Code documentation -- Official docs for hooks, CLAUDE.md, and configuration
  • Claude Usage Tracker -- Longitudinal usage reports by @hamed-elfayome, complements the bundled statusline
  • Johnny Decimal -- The numbering system used for memory organization
  • Conventional Commits -- The commit format enforced by the starter hooks

Your fort. Your colors.

A neighborhood of forts in different palettes

MIT License

About

A template for building persistent AI workspaces on top of Claude Code

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages