Skip to content

System Prompts & CLAUDE.md

Shape Claude's behavior with system prompts and project config

11 min read

You set --system-prompt to add a coding rule. It works — but now Claude has forgotten its safety guidelines, its code style preferences, and every instruction from your CLAUDE.md files. You replaced the entire system prompt when you meant to append to it.

There are three ways to give Claude instructions beyond the prompt: --append-system-prompt adds rules on top of the defaults, --system-prompt replaces the defaults entirely, and CLAUDE.md files provide persistent project-level configuration that survives compaction. Choosing the right one determines whether Claude keeps its safety guidelines, how much you pay per call, and whether your rules stick around across sessions.

—append-system-prompt

This is the safe, additive approach. Your text is appended to the end of the default system prompt, so Claude keeps all of its built-in safety guidelines, tool instructions, and coding conventions. Your rules are layered on top.

Appending to the Default Prompt
# Append a rule — defaults stay intact
$ claude -p "Say: I am using an appended prompt" \ --append-system-prompt "Always end your response with 'CUSTOM_TAG'"
I am using an appended prompt CUSTOM_TAG
# Safety guidelines, tool access, and coding conventions are all preserved

The appended text becomes part of the system prompt that Claude sees, but nothing is removed. This is the right choice for most use cases: forcing plan mode for complex requests, adding code review criteria, injecting domain-specific rules like “always use TypeScript strict mode,” or setting output format constraints.

Terminal window
# Force plan-first behavior for complex tasks
claude -p "Refactor the auth module" \
--append-system-prompt "When the user brings a list of changes, \
immediately call EnterPlanMode and research the codebase before presenting a plan."
# Security review with structured output rules
claude -p "Review auth.py" \
--append-system-prompt "Focus exclusively on SQL injection and XSS vulnerabilities. \
Rate each finding as Critical, High, Medium, or Low."

—system-prompt

This flag replaces the entire default Claude Code system prompt. Everything built in — safety guidelines, tool usage instructions, coding conventions, permission handling rules, git safety protocols, tone and style directives — is gone. Claude becomes a blank slate that follows only what you provide.

Full System Prompt Override
# Full override — Claude is now just a pirate
$ claude -p "Say: I am using a custom system prompt" \ --system-prompt "You are a pirate. Always respond in pirate speak."
Ahoy there, matey! I be usin' a custom system prompt, arr!
Gotcha

—system-prompt removes all safety guidelines. The default system prompt contains critical rules: do not delete files without asking, git safety protocols, permission handling, and more. Overriding it removes ALL of these. Use —append-system-prompt unless you specifically need a blank-slate persona like a chatbot or tutor.

Full override is the right tool when you want Claude to be a completely different persona — a Gandalf chatbot, a language tutor, a pure JSON transformer. Pair it with --tools "" to disable file system access and create a sandboxed persona:

Terminal window
# Pure persona with no tool access
claude -p "Tell me about yourself" \
--system-prompt "You are Gandalf the Grey. Speak in character." \
--tools ""

CLAUDE.md

Both --system-prompt and --append-system-prompt are ephemeral. They apply only to the current claude -p invocation and are not preserved when you --resume a session. For persistent, project-level instructions, use CLAUDE.md.

A CLAUDE.md file is loaded from disk at session start and re-read after compaction. This means your rules survive the /compact command and long sessions that trigger auto-compaction. Place the file in your project root, and every session launched from that directory inherits the instructions.

CLAUDE.md
## Coding Standards
- Use TypeScript strict mode for all new files
- Prefer named exports over default exports
- All functions must have JSDoc comments
## Git Rules
- Never commit directly to main
- Prefix commit messages with feat:, fix:, or chore:

Persistence Comparison

SourceWhen LoadedSurvives /compactScope
—system-promptSession startN/A (ephemeral)Single invocation
—append-system-promptSession startN/A (ephemeral)Single invocation
CLAUDE.mdSession start + after compactionYes (re-read from disk)Project-persistent
Tip

Keep CLAUDE.md under 200 lines. Long files consume significant context and Claude may not follow all instructions. Put your most important rules at the top.

When to Use Which

Choosing the Right Mechanism

SituationUseWhy
One-off CI job with custom rules—append-system-promptKeeps safety defaults, adds your rules for one run
Blank-slate persona (chatbot, tutor)—system-promptReplaces defaults so the persona is not diluted
Project-wide conventions for all sessionsCLAUDE.mdPersists across sessions and survives compaction
Temporary rule for a single invocation—append-system-promptNo side effects, no file changes needed
Rules that must survive /compactCLAUDE.mdRe-read from disk after compaction
CI security review pipeline—append-system-promptAdd reviewer persona while keeping tool access and safety
Try This

See the difference yourself. First, append:

claude -p “What instructions do you have?” —append-system-prompt “Always respond in exactly one sentence.”

Now replace:

claude -p “What instructions do you have?” —system-prompt “Always respond in exactly one sentence.”

Notice how —system-prompt strips out Claude’s default instructions, while —append-system-prompt keeps them. Which response is more useful?

Token Cost: Override vs Append

The system prompt mechanism you choose has a direct impact on token counts and cost. A full override with a short prompt uses fewer cached tokens than the ~14K default. Appending creates new cache entries for the combined prompt.

Full Override (Pirate Mode)artifacts/07/system_prompt_override.json
1{
2 "type": "result",
3 "subtype": "success",
4 "is_error": false,
5 "duration_ms": 3668,
6 "num_turns": 1,
7 "result": "\n\nAhoy there, matey!\n\nI be usin' a custom system prompt, arr! That be the truth of it, straight from me captain's quarters! Now, what adventure can this ol' sea dog help ye with today?",A
8 "session_id": "b40bfb47-653f-4081-876b-038d013458f2",
9 "total_cost_usd": 0.014833,
10 "usage": {
11 "input_tokens": 3,B
12 "cache_creation_input_tokens": 1398,
13 "cache_read_input_tokens": 8811,
14 "output_tokens": 67
15 }C
16}
APirate persona -- full override worked
B$0.015 -- cheaper than normal due to smaller system prompt
C8,811 cached tokens vs ~14K default -- smaller prompt, fewer tokens
Append Mode (CUSTOM_TAG Rule)artifacts/07/append_system_prompt.json
1{
2 "type": "result",
3 "subtype": "success",
4 "is_error": false,
5 "duration_ms": 6441,
6 "num_turns": 1,
7 "result": "I am using an appended prompt\n\nCUSTOM_TAG",A
8 "session_id": "2a3ce8b9-23c4-4fce-8cb2-e4eb16859131",
9 "total_cost_usd": 0.061331,
10 "usage": {
11 "input_tokens": 3,B
12 "cache_creation_input_tokens": 9124,
13 "cache_read_input_tokens": 6532,
14 "output_tokens": 41C
15 }
16}
AResponse ends with CUSTOM_TAG -- appended rule was followed
B$0.061 -- more expensive (larger combined prompt)
C9,124 creation tokens -- new combined prompt being cached for the first time

The override call costs $0.015 with 8,811 cached read tokens. The append call costs $0.061 with 9,124 cache creation tokens because the combined default-plus-appended prompt had to be cached fresh. On subsequent calls with the same appended text, the creation cost drops as the cache is reused.

All Three Layers Compose

A common misconception: --system-prompt replaces CLAUDE.md. It does not. All three context layers are active simultaneously:

Prompt Composition Stack
--append-system-prompt adds on top keeps defaults CLAUDE.md project-level config survives compaction Default system prompt safety guidelines ~14K tokens, cached READS BOTTOM → TOP --system-prompt replaces ALL layers — use --append-system-prompt instead

System Prompt Stack (Experimentally Verified)

LayerSourceRemoved by —system-prompt?
Base system promptBuilt-in default OR —system-promptReplaced (this is what the flag replaces)
CLAUDE.md hierarchyAll CLAUDE.md files up the directory treeNo — always loaded, no opt-out
Append prompt—append-system-promptNo — appended after everything

Testing with --system-prompt "pirate speak" + --append-system-prompt "end with ARRR" + a CLAUDE.md containing “mention TEST_PROJECT”, all three instructions were followed in every response. The effective context stack is: [--system-prompt OR default] + [CLAUDE.md hierarchy] + [--append-system-prompt].

Note

There is no flag to disable CLAUDE.md loading. No —system-prompt, no —disable-slash-commands, no .git boundary prevents it. The only isolation mechanism is separate repositories.

Gotchas

Gotcha

Full override removes safety guidelines. The default system prompt includes rules like “do not delete files without asking” and git safety protocols. When you use —system-prompt, all of these disappear. Claude will happily rm -rf if asked. Only use full override when you have deliberately decided to take responsibility for safety yourself.

Gotcha

MCP tool descriptions survive a full override. MCP tools (Chrome DevTools, Notion, etc.) are injected separately from the system prompt. Even with —system-prompt “short” —tools "", a test showed 22,295 cached read tokens because MCP descriptions remained in the context. You cannot remove them with system prompt flags alone.

Tip

—append-system-prompt is ephemeral in —print mode. It is not preserved when you —resume a session. If you need rules that persist across session resumptions, put them in CLAUDE.md.

CLAUDE.md Sizing Guidance

CLAUDE.md files are loaded into context on every session start and survive compaction. Their size directly impacts startup cost, response quality, and first-response latency.

Recommended limits:

  • Under 10,000 words for a single CLAUDE.md — community consensus for optimal performance
  • 2,800+ lines causes measurable degradation — ~2,100 additional startup tokens and 10-12 second first-response delay
  • 47,000 words is catastrophic — community reports show dramatic performance drops at this scale

Monorepo splitting strategy: Instead of one massive root CLAUDE.md, split into subdirectory-specific files:

project/
├── CLAUDE.md # 2,000 words — global conventions only
├── frontend/CLAUDE.md # 3,000 words — React patterns, component structure
├── backend/CLAUDE.md # 3,000 words — API conventions, database patterns
└── infrastructure/CLAUDE.md # 2,000 words — deployment, CI/CD specifics

Claude loads all CLAUDE.md files in the directory hierarchy (from root to cwd). Subdirectory files add to — not replace — parent files. Only the CLAUDE.md files on the path to your current working directory are loaded, so a frontend developer does not pay for backend CLAUDE.md tokens.

CLAUDE.md Over 10K Words Degrades Performance

There is no hard limit on CLAUDE.md size, but community testing shows measurable degradation beyond 10,000 words: slower first responses, higher startup token costs, and reduced instruction-following accuracy as Claude’s attention is spread across too many directives. Split large files into subdirectory-specific CLAUDE.md files to keep each under 10K words while maintaining full coverage.

Now Do This

Add —append-system-prompt “Be concise. Respond in under 50 words.” to one of your automation scripts. Verify Claude still follows its default safety guidelines while also respecting your added rule. This is almost always what you want instead of —system-prompt.