claude-codestart is built on a deliberate separation of concerns. Understanding the four layers tells you exactly where each piece of your context lives, why, and when it gets loaded.
┌──────────────────────────────────────────────────────────────┐
│ LAYER 1 — STATIC CORE │
│ File: ~/.claude/CLAUDE.md │
│ Size: ~5–15 KB (~1–4k tokens) │
│ Loaded: automatically on every session (Claude Code built-in)│
│ Contains: identity, locked rules, stable project paths │
│ Changes: rarely (edited by hand when rules evolve) │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ LAYER 2 — DYNAMIC PULSE ◄── this is what claude- │
│ File: ~/.claude-codestart/PULSE.md │ codestart provides
│ Size: ~20–100 KB (~5–25k tokens) │
│ Loaded: via SessionStart hook (claude-codestart's scope) │
│ Contains: today's state, recent journal, open loops, live │
│ git/build/PR state — anything time-sensitive │
│ Changes: on every session start (rebuilt by build-pulse.sh) │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ LAYER 3 — MANIFEST │
│ Location: a dedicated section in ~/.claude/CLAUDE.md │
│ Size: ~2–3 KB │
│ Loaded: along with Layer 1 │
│ Contains: file tree with one-line summaries of everything │
│ in your knowledge system │
│ Purpose: teaches the LLM WHERE to look when it needs detail │
│ Changes: monthly or when the filesystem layout evolves │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ LAYER 4 — ON-DEMAND │
│ Tools: Read, Grep, Glob, MCP filesystem, (optional) Mem0 │
│ Loaded: only when the LLM explicitly reaches for detail │
│ Contains: everything else — the full knowledge base, │
│ codebase, historical notes, archived data │
│ Purpose: pay-per-read access to detail the pulse didn't │
│ fit │
└──────────────────────────────────────────────────────────────┘
Because different kinds of information have different update frequencies and different value densities.
- Identity doesn't change daily → put it in Layer 1 (cheap, stable, auto-loaded).
- Today's state changes constantly → put it in Layer 2 (rebuilt per session).
- Filesystem layout teaches the LLM where to look → put it in Layer 3 (cheap, rarely changes).
- Everything else is too big to preload → put it in Layer 4 (read on demand).
Putting the wrong thing in the wrong layer is the main failure mode:
- Layer 1 with today's state → stale within hours, expensive to edit.
- Layer 2 with your whole knowledge base → blows the context budget.
- Layer 4 with today's state → Claude can't find what it didn't know to ask for.
claude-codestart is specifically the tooling for Layer 2. Layers 1 and 3 you build by hand in your CLAUDE.md. Layer 4 is already built into Claude Code (Read, Grep, etc.).
A Claude Sonnet 4.5 session has ~200k tokens of usable context. Here's the rough allocation we recommend:
| Layer | Budget | Rationale |
|---|---|---|
| Layer 1 (Static Core) | ~5–15k tokens | Small and stable. Don't over-stuff it. |
| Layer 2 (Dynamic Pulse) | ~15–25k tokens | Big enough for today, small enough to leave room for work. |
| Layer 3 (Manifest) | ~2–3k tokens | Tiny pointer list, huge leverage. |
| Layer 4 (On-demand) | N/A | Loaded as tool calls; counts against remaining budget. |
| Working context for actual conversation | ~160k tokens | What's left after the above. |
Hard rule: the Layer 2 pulse should never exceed 25% of your token budget. If it does, you're stuffing things into the pulse that belong in Layer 4 (on-demand). Trim.
The included build-pulse.sh template has a soft cap via CODESTART_MAX_BYTES (default 100 KB ≈ 25k tokens). It will warn in the output if you exceed it but will not refuse to build.
| Layer | When it rebuilds | How |
|---|---|---|
| Layer 1 | When you edit CLAUDE.md |
Manual |
| Layer 2 | Every session start | SessionStart hook runs your build-pulse.sh |
| Layer 2 (optional) | Every 30 min in the background | Add a cron job that runs build-pulse.sh |
| Layer 2 (optional) | On filesystem change | fswatch on your journal/status dirs |
| Layer 3 | When you add or rearrange major knowledge files | Manual |
| Layer 4 | Never — it's always on disk | Always current |
claude-codestart v0.0.1 ships with SessionStart-only refresh. That's sufficient for the common case because each new session gets a fresh pulse. Cron and fswatch are opt-in add-ons for users who want PULSE.md to stay fresh on disk for inspection or non-Claude consumers.
Important caveat: background rebuilds (cron/fswatch) do NOT update a currently-running session's context. A session's context is captured at startup. To get new data into a running session you must /compact, /clear, or start a new session.
We chose markdown-first for five concrete reasons:
- Traceability. Every claim in the pulse is a direct quote from a file. You can
grepfor it. Vector similarity is opaque — you can't audit what the model "remembered." - Edit-ability. You can edit your pulse sources with any editor, on any machine, with any tool. Vector DBs require a re-indexing step after every change.
- Determinism. The same state files always produce the same pulse. Vector retrieval has inherent nondeterminism from approximate nearest neighbor search.
- Zero dependencies. Bash + jq. That's it. No daemons, no databases, no cloud services, no API keys to leak.
- The LLM is already a markdown reader. Claude is literally trained on markdown. Feeding it well-structured markdown is the minimum-friction interface. Adding a vector DB adds a translation step that you then have to debug.
Vector databases and memory frameworks (Mem0, Letta, Zep) are excellent for passive fact extraction from conversation history — a different use case that claude-codestart does not attempt to solve. If you want both, run them side by side: claude-codestart handles "here is today's state," a memory framework handles "what did Rudy mention three weeks ago that I should remember." They compose cleanly.
claude-codestart v0.0.1 is single-user by design. Each installation points at one user's state files.
For multi-tenant systems (a SaaS where every user gets their own warm-start experience), the architecture transfers cleanly:
- Each user has their own
build-pulse.shscript + source files (sandboxed per user) - The SessionStart hook becomes a server-side middleware that injects the user's pulse into their chat session
- PULSE.md becomes a per-user document stored in your user storage layer
The generator logic and the 4-layer model are identical. Only the runtime is different. We may ship a claude-codestart-saas reference implementation in a future release — if that's interesting to you, open an issue.
- Installation guide — step-by-step for macOS and Linux
- Andrej Karpathy's LLM-maintained wiki pattern — the philosophical foundation for markdown-first agent state
- Claude Code hooks documentation — the official spec for SessionStart and other lifecycle hooks